pax_global_header00006660000000000000000000000064152321643520014515gustar00rootroot0000000000000052 comment=f5c30d0490fb7bcd8e0b65d8d8e63c0e7d1bfe59 anthropic-sdk-python-0.120.2/000077500000000000000000000000001523216435200157445ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/.devcontainer/000077500000000000000000000000001523216435200205035ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/.devcontainer/Dockerfile000066400000000000000000000003751523216435200225020ustar00rootroot00000000000000ARG VARIANT="3.9" FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT} USER vscode COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ RUN echo "[[ -d .venv ]] && source .venv/bin/activate || export PATH=\$PATH" >> /home/vscode/.bashrc anthropic-sdk-python-0.120.2/.devcontainer/devcontainer.json000066400000000000000000000023011523216435200240530ustar00rootroot00000000000000// For format details, see https://aka.ms/devcontainer.json. For config options, see the // README at: https://github.com/devcontainers/templates/tree/main/src/debian { "name": "Debian", "build": { "dockerfile": "Dockerfile", "context": ".." }, "postStartCommand": "uv sync --all-extras", "customizations": { "vscode": { "extensions": [ "ms-python.python" ], "settings": { "terminal.integrated.shell.linux": "/bin/bash", "python.pythonPath": ".venv/bin/python", "python.defaultInterpreterPath": ".venv/bin/python", "python.typeChecking": "basic", "terminal.integrated.env.linux": { "PATH": "${env:PATH}" } } } }, "features": { "ghcr.io/devcontainers/features/node:1": {} } // Features to add to the dev container. More info: https://containers.dev/features. // "features": {}, // Use 'forwardPorts' to make a list of ports inside the container available locally. // "forwardPorts": [], // Configure tool-specific properties. // "customizations": {}, // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. // "remoteUser": "root" } anthropic-sdk-python-0.120.2/.github/000077500000000000000000000000001523216435200173045ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/.github/CODEOWNERS000066400000000000000000000003141523216435200206750ustar00rootroot00000000000000# This file is used to automatically assign reviewers to PRs # For more information see: https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners * @anthropics/sdk anthropic-sdk-python-0.120.2/.github/workflows/000077500000000000000000000000001523216435200213415ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/.github/workflows/ci.yml000066400000000000000000000107031523216435200224600ustar00rootroot00000000000000name: CI on: push: branches: - '**' - '!integrated/**' - '!stl-preview-head/**' - '!stl-preview-base/**' - '!generated' - '!codegen/**' - 'codegen/stl/**' pull_request: branches-ignore: - 'stl-preview-head/**' - 'stl-preview-base/**' jobs: lint: timeout-minutes: 10 name: lint runs-on: 'ubuntu-latest' if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install uv uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 with: version: '0.10.2' - name: Install dependencies run: uv sync --all-extras - name: Run lints run: ./scripts/lint build: if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) timeout-minutes: 10 name: build permissions: contents: read id-token: write runs-on: 'ubuntu-latest' steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install uv uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 with: version: '0.10.2' - name: Install dependencies run: uv sync --all-extras - name: Run build run: uv build - name: Get GitHub OIDC Token if: |- github.repository == 'anthropics/anthropic-sdk-python-private' && !startsWith(github.ref, 'refs/heads/stl/') id: github-oidc uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: script: core.setOutput('github_token', await core.getIDToken()); - name: Upload tarball if: |- github.repository == 'anthropics/anthropic-sdk-python-private' && !startsWith(github.ref, 'refs/heads/stl/') env: URL: https://pkg.stainless.com/s AUTH: ${{ steps.github-oidc.outputs.github_token }} SHA: ${{ github.sha }} run: ./scripts/utils/upload-artifact.sh test: timeout-minutes: 10 name: test runs-on: 'ubuntu-latest' if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install uv uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 with: version: '0.10.2' - name: Bootstrap run: ./scripts/bootstrap - name: Run tests run: ./scripts/test detect_breaking_changes_vs_main: timeout-minutes: 10 name: detect-breaking-changes-vs-main runs-on: ${{ github.repository == 'stainless-sdks/anthropic-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: |- (github.event_name == 'push' && !startsWith(github.ref, 'refs/heads/release-please--')) || github.event.pull_request.head.repo.fork steps: # fetch-depth: 0 fetches full commit history (all branches, so # origin/main is available) so `git merge-base HEAD origin/main` # below can resolve the fork point. filter: blob:none keeps the # fetch cheap: commit metadata is downloaded eagerly, file blobs # only when a later `git checkout` asks for them. - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 filter: blob:none - name: Install uv uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 with: version: '0.10.2' - name: Install dependencies run: uv sync --all-extras - name: Determine base SHA run: | BASE_SHA=$(git merge-base HEAD origin/main 2>/dev/null || echo "") echo "BASE_SHA=$BASE_SHA" >> $GITHUB_ENV - name: Detect removed symbols if: env.BASE_SHA != '' run: | uv run python scripts/detect-breaking-changes.py "$BASE_SHA" - name: Detect breaking changes vs. main if: env.BASE_SHA != '' run: | # Try to check out previous versions of the breaking change detection script. This ensures that # we still detect breaking changes when entire files and their tests are removed. git checkout "$BASE_SHA" -- ./scripts/detect-breaking-changes 2>/dev/null || true ./scripts/detect-breaking-changes "$BASE_SHA" anthropic-sdk-python-0.120.2/.github/workflows/claude.yml000066400000000000000000000035271523216435200233300ustar00rootroot00000000000000name: Claude Code on: issue_comment: types: [created] pull_request_review_comment: types: [created] issues: types: [opened, assigned] pull_request_review: types: [submitted] jobs: claude: if: | (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) || (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) || (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) || (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) runs-on: ubuntu-latest permissions: contents: write pull-requests: write issues: write id-token: write actions: read # Required for Claude to read CI results on PRs steps: - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 - name: Configure git run: | git config user.name "claude[bot]" git config user.email "209825114+claude[bot]@users.noreply.github.com" - name: Run Claude Code id: claude uses: anthropics/claude-code-action@v1 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} # Allow Claude to run git commands and push changes allowed_tools: "Bash(git commit:*),Bash(git push:*),Bash(git merge:*),Bash(git checkout:*),Bash(git add:*),Bash(git status:*),Bash(git diff:*),Bash(git log:*),Bash(git fetch:*),Bash(gh pr:*),Bash(gh issue:*)" # Allow github-actions[bot] to trigger Claude Code Action allowed_bots: "stainless-app" # This is an optional setting that allows Claude to read CI results on PRs additional_permissions: | actions: read anthropic-sdk-python-0.120.2/.github/workflows/create-releases.yml000066400000000000000000000020601523216435200251260ustar00rootroot00000000000000name: Create releases on: schedule: - cron: '0 5 * * *' # every day at 5am UTC push: branches: - main jobs: release: name: release if: github.ref == 'refs/heads/main' && github.repository == 'anthropics/anthropic-sdk-python' runs-on: ubuntu-latest environment: production-release permissions: contents: read id-token: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: stainless-api/trigger-release-please@bb6677c5a04578eec1ccfd9e1913b5b78ed64c61 # v1.4.0 id: release with: repo: ${{ github.event.repository.full_name }} stainless-api-key: ${{ secrets.STAINLESS_API_KEY }} - name: Install uv if: ${{ steps.release.outputs.releases_created }} uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 with: version: '0.9.13' - name: Publish to PyPI if: ${{ steps.release.outputs.releases_created }} run: | bash ./bin/publish-pypi anthropic-sdk-python-0.120.2/.github/workflows/publish-pypi.yml000066400000000000000000000013441523216435200245130ustar00rootroot00000000000000# workflow for re-running publishing to PyPI in case it fails for some reason # you can run this workflow by navigating to https://www.github.com/anthropics/anthropic-sdk-python/actions/workflows/publish-pypi.yml name: Publish PyPI on: workflow_dispatch: jobs: publish: name: publish runs-on: ubuntu-latest environment: production-release permissions: contents: read id-token: write steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install uv uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2 with: version: '0.9.13' - name: Publish to PyPI run: | bash ./bin/publish-pypi anthropic-sdk-python-0.120.2/.gitignore000066400000000000000000000001621523216435200177330ustar00rootroot00000000000000.prism.log .stdy.log _dev __pycache__ .mypy_cache dist .venv .idea .env .envrc codegen.log Brewfile.lock.json anthropic-sdk-python-0.120.2/.inline-snapshot/000077500000000000000000000000001523216435200211355ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/.inline-snapshot/external/000077500000000000000000000000001523216435200227575ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/.inline-snapshot/external/.gitignore000066400000000000000000000001041523216435200247420ustar00rootroot00000000000000# ignore all snapshots which are not referred in the source *-new.* anthropic-sdk-python-0.120.2/.python-version000066400000000000000000000000071523216435200207460ustar00rootroot000000000000003.9.18 anthropic-sdk-python-0.120.2/.release-please-manifest.json000066400000000000000000000000241523216435200234040ustar00rootroot00000000000000{ ".": "0.120.2" }anthropic-sdk-python-0.120.2/.stats.yml000066400000000000000000000004421523216435200177030ustar00rootroot00000000000000configured_endpoints: 131 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/anthropic/anthropic-6d5c96a475b06ff067a4491fc2258181f0426af6c64bcfd4be5d167a8c0d9d16.yml openapi_spec_hash: d2deb0fef6a15bf53cc6c53f07973a54 config_hash: 447f76a00c2affe40a8b4c43129e17a3 anthropic-sdk-python-0.120.2/.vscode/000077500000000000000000000000001523216435200173055ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/.vscode/settings.json000066400000000000000000000000641523216435200220400ustar00rootroot00000000000000{ "python.analysis.importFormat": "relative", } anthropic-sdk-python-0.120.2/Brewfile000066400000000000000000000000131523216435200174200ustar00rootroot00000000000000brew "uv" anthropic-sdk-python-0.120.2/CHANGELOG.md000066400000000000000000006446231523216435200175740ustar00rootroot00000000000000# Changelog ## 0.120.2 (2026-07-28) Full Changelog: [v0.120.1...v0.120.2](https://github.com/anthropics/anthropic-sdk-python/compare/v0.120.1...v0.120.2) ### Bug Fixes * **mcp:** support mcp sdk v2 alongside v1 ([#300](https://github.com/anthropics/anthropic-sdk-python/issues/300)) ([177f88c](https://github.com/anthropics/anthropic-sdk-python/commit/177f88ccd7f966e47b654cb19ad0e9cfa4c58ac2)) ## 0.120.1 (2026-07-28) Full Changelog: [v0.120.0...v0.120.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.120.0...v0.120.1) ### Bug Fixes * **mcp:** pin mcp extra to <2 ([#1783](https://github.com/anthropics/anthropic-sdk-python/issues/1783)) ([fb66371](https://github.com/anthropics/anthropic-sdk-python/commit/fb66371322621be23a0956f1998da39c4686f4cb)) ## 0.120.0 (2026-07-24) Full Changelog: [v0.119.0...v0.120.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.119.0...v0.120.0) ### Features * **api:** add claude-opus-5 model ([bf4e31c](https://github.com/anthropics/anthropic-sdk-python/commit/bf4e31c17c43ba2b409bec57e1856b025d159f1a)) * **api:** add tool addition/removal blocks and tool_change events ([bf4e31c](https://github.com/anthropics/anthropic-sdk-python/commit/bf4e31c17c43ba2b409bec57e1856b025d159f1a)) * **api:** expand client-side fallback credit token types and add server-side fallbacks default option ([bf4e31c](https://github.com/anthropics/anthropic-sdk-python/commit/bf4e31c17c43ba2b409bec57e1856b025d159f1a)) ## 0.119.0 (2026-07-23) Full Changelog: [v0.118.0...v0.119.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.118.0...v0.119.0) ### Features * **api:** add new stop reason 'model_context_window_exceeded' ([d983cde](https://github.com/anthropics/anthropic-sdk-python/commit/d983cdecdea27fb8ae36fd293930b978c9bebf4f)) ### Bug Fixes * **tools:** handle binary files in agent toolset read/edit ([#283](https://github.com/anthropics/anthropic-sdk-python/issues/283)) ([417b76b](https://github.com/anthropics/anthropic-sdk-python/commit/417b76b9adcb69ee9effa2e9af1536dd08e6764e)) ## 0.118.0 (2026-07-22) Full Changelog: [v0.117.1...v0.118.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.117.1...v0.118.0) ### Features * **api:** add support for Managed Agents model effort, initial session events, and threads delta streaming ([712bc6f](https://github.com/anthropics/anthropic-sdk-python/commit/712bc6f5e07ca7607e13f24fba4eea57c2e73478)) ## 0.117.1 (2026-07-21) Full Changelog: [v0.117.0...v0.117.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.117.0...v0.117.1) ### Bug Fixes * **aws:** handle credentials correctly when using AnthropicAWS.copy() ([85d3881](https://github.com/anthropics/anthropic-sdk-python/commit/85d3881b2e178dd30b30f9ac4c462f20ea88693d)) ### Chores * **api:** add support for new refusal category ([d1dea0b](https://github.com/anthropics/anthropic-sdk-python/commit/d1dea0b51164c859de1e05a78468f5be2b6a67de)) * **client:** docs updates ([b14f94c](https://github.com/anthropics/anthropic-sdk-python/commit/b14f94c231f9adc6bb2ad96eddfc27cc1a4cfea7)) * **deps:** bump http-snapshot to 0.1.9 ([#275](https://github.com/anthropics/anthropic-sdk-python/issues/275)) ([434b657](https://github.com/anthropics/anthropic-sdk-python/commit/434b65772633f1c0d1899de64d40a67b7cb943fe)) * **deps:** pin httpx_aiohttp major version ([#271](https://github.com/anthropics/anthropic-sdk-python/issues/271)) ([924487f](https://github.com/anthropics/anthropic-sdk-python/commit/924487f7c8b3afd69ec382af78326ba224779141)) * **docs:** small updates ([c48db8b](https://github.com/anthropics/anthropic-sdk-python/commit/c48db8b6cdd800a98bf57088f530de8fe4dc82e8)) * **docs:** small updates ([755c06c](https://github.com/anthropics/anthropic-sdk-python/commit/755c06cc06161ccdf9b28b77d5e6de58adb462ce)) * **internal:** codegen related update ([a4fbecf](https://github.com/anthropics/anthropic-sdk-python/commit/a4fbecf6a2b002c8807da2336a75ddef7864f47c)) ## 0.117.0 (2026-07-16) Full Changelog: [v0.116.0...v0.117.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.116.0...v0.117.0) ### Features * **api:** add support for dreaming ([642eee7](https://github.com/anthropics/anthropic-sdk-python/commit/642eee707b0fe3c35c21917f60b06846b2a3b291)) * **api:** add support for MCP Tunnels ([d716df6](https://github.com/anthropics/anthropic-sdk-python/commit/d716df6edb1df7de25f1b5e14dcab33b9aafc2ff)) ### Bug Fixes * **credentials:** keep credential material out of traceback frame locals via SecretStr ([aa93a4d](https://github.com/anthropics/anthropic-sdk-python/commit/aa93a4dbf383f7fdab1404c136e86d4679d644c1)) ### Chores * **docs:** small updates to field descriptions ([75d8dcc](https://github.com/anthropics/anthropic-sdk-python/commit/75d8dcc0ac8cb381fa327a61a9ac84022b3a1677)) * **docs:** update model example ([a57e30a](https://github.com/anthropics/anthropic-sdk-python/commit/a57e30ac1bafb54387bf631be2081bfdb3782911)) * **docs:** updates to descriptions and examples ([e1535b6](https://github.com/anthropics/anthropic-sdk-python/commit/e1535b6919441448dddf00e2636cb8c60a721a43)) ## 0.116.0 (2026-07-02) Full Changelog: [v0.115.1...v0.116.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.115.1...v0.116.0) ### Features * **api:** add agent-memory-2026-07-22 beta header ([e181d5c](https://github.com/anthropics/anthropic-sdk-python/commit/e181d5c1b233d5b0b313c78b27cf1dd27f620e74)) ## 0.115.1 (2026-07-01) Full Changelog: [v0.115.0...v0.115.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.115.0...v0.115.1) ### Chores * **api:** remove some nonfunctional types from the SDKs ([5e7c431](https://github.com/anthropics/anthropic-sdk-python/commit/5e7c431ef31b72b3f1f59902e678316fea14d983)) ## 0.115.0 (2026-06-30) Full Changelog: [v0.114.0...v0.115.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.114.0...v0.115.0) ### Features * **api:** add support for Managed Agents event delta streaming, agent overrides, reverse pagination, vault credential injection scoping, and agent and deployment webhook events ([8c23f7e](https://github.com/anthropics/anthropic-sdk-python/commit/8c23f7ef103c287362364c12503de85eb31f07fb)) ## 0.114.0 (2026-06-30) Full Changelog: [v0.113.0...v0.114.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.113.0...v0.114.0) ### Features * **api:** add support for claude-sonnet-5 ([b893033](https://github.com/anthropics/anthropic-sdk-python/commit/b893033b32951e0e2e04afa36a3a7eb016ae4b99)) ### Bug Fixes * **agent_toolset:** allow absolute paths that resolve inside workdir ([#121](https://github.com/anthropics/anthropic-sdk-python/issues/121)) ([0105529](https://github.com/anthropics/anthropic-sdk-python/commit/0105529fe15b1f80bbf9c56f4ae684fdfa10e2b3)) ## 0.113.0 (2026-06-29) Full Changelog: [v0.112.0...v0.113.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.112.0...v0.113.0) ### Features * **api:** add support for 20260318 web fetch and support tools ([88dbfb1](https://github.com/anthropics/anthropic-sdk-python/commit/88dbfb14a2a838eda889469ad7fe07a47618e85f)) ### Bug Fixes * async count_tokens missing output_format/output_config merge block ([#162](https://github.com/anthropics/anthropic-sdk-python/issues/162)) ([122c958](https://github.com/anthropics/anthropic-sdk-python/commit/122c95811566bf6f5cbc682ae0a74972ae75a223)) ### Chores * **api:** accept user profile ID's when counting tokens ([0b4d17a](https://github.com/anthropics/anthropic-sdk-python/commit/0b4d17a49d39e8224adbee6a97be0e8b1b7ebff5)) * **docs:** updates to descriptions and example values ([f3ab694](https://github.com/anthropics/anthropic-sdk-python/commit/f3ab694453326a2765623b9aafeb7588ea296325)) ## 0.112.0 (2026-06-24) Full Changelog: [v0.111.0...v0.112.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.111.0...v0.112.0) ### Features * **client:** add support for system.message streaming events ([2450d59](https://github.com/anthropics/anthropic-sdk-python/commit/2450d595731f9532080bb94eb8a43c0bd5189659)) ### Bug Fixes * **memory tool:** create parent directories with the correct permissions ([#135](https://github.com/anthropics/anthropic-sdk-python/issues/135)) ([f2fc2a9](https://github.com/anthropics/anthropic-sdk-python/commit/f2fc2a9e0ad8507e4108e9a6b85d023416c2f14c)) ### Chores * **api:** add support for new refusal category ([5ab533e](https://github.com/anthropics/anthropic-sdk-python/commit/5ab533e58ee99bcd2e5071bab91d99caee66aa6a)) * **api:** add support for sending User Profile ID in request headers ([83319be](https://github.com/anthropics/anthropic-sdk-python/commit/83319bed74f4414d54e0f4237d70b945ed671008)) ## 0.111.0 (2026-06-18) Full Changelog: [v0.110.0...v0.111.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.110.0...v0.111.0) ### Features * **helpers:** tag refusal-fallback middleware requests with fallback-refusal-middleware ([#96](https://github.com/anthropics/anthropic-sdk-python/issues/96)) ([2f8ac78](https://github.com/anthropics/anthropic-sdk-python/commit/2f8ac789506efc0719b06f1f646c9a98bb25ce7b)) ## 0.110.0 (2026-06-18) Full Changelog: [v0.109.2...v0.110.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.109.2...v0.110.0) ### Features * **api:** add support for new code_execution_20260120 tool ([5e23212](https://github.com/anthropics/anthropic-sdk-python/commit/5e23212dc0883174c879b97ef8e7e33ead4e8da5)) ### Bug Fixes * append x-stainless-helper across header merges instead of clobbering ([#105](https://github.com/anthropics/anthropic-sdk-python/issues/105)) ([922558e](https://github.com/anthropics/anthropic-sdk-python/commit/922558e2ce52e18863dab27bcc04067068827364)) * **bedrock:** preserve stream event type ([#1682](https://github.com/anthropics/anthropic-sdk-python/issues/1682)) ([b27e343](https://github.com/anthropics/anthropic-sdk-python/commit/b27e3439699174dbc41e34e2d6ef5cb1e2930c18)) * **helpers:** single source of truth for x-stainless-helper key + closed value vocabulary ([#95](https://github.com/anthropics/anthropic-sdk-python/issues/95)) ([e6f7a56](https://github.com/anthropics/anthropic-sdk-python/commit/e6f7a56bb624f4c946cb15ba7973fd6fe052e10f)) ## 0.109.2 (2026-06-15) Full Changelog: [v0.109.1...v0.109.2](https://github.com/anthropics/anthropic-sdk-python/compare/v0.109.1...v0.109.2) ### Chores * **api:** remove retired models from API and SDKs ([d4bcfcc](https://github.com/anthropics/anthropic-sdk-python/commit/d4bcfcc257bd0c97d5e75060bd19c97abddd9f49)) ## 0.109.1 (2026-06-09) Full Changelog: [v0.109.0...v0.109.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.109.0...v0.109.1) ### Bug Fixes * **api:** add `frontier_llm` refusal category ([d3a806b](https://github.com/anthropics/anthropic-sdk-python/commit/d3a806b454d8aaf5806db11c651deebe61836131)) ## 0.109.0 (2026-06-09) Full Changelog: [v0.108.0...v0.109.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.108.0...v0.109.0) ### Features * **api:** add support for Managed Agents deployments and environment variable credentials ([47633bf](https://github.com/anthropics/anthropic-sdk-python/commit/47633bff658d4aaced3cd920ef6782c48cf31a9a)) ## 0.108.0 (2026-06-09) Full Changelog: [v0.107.1...v0.108.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.107.1...v0.108.0) ### Features * **api:** add support for claude-mythos-5 and claude-fable-5, with support for server-side fallbacks on refusal ([6b76649](https://github.com/anthropics/anthropic-sdk-python/commit/6b76649f99bd782d2300f2a6aa3f4a3f040af324)) * **client:** adds client-side fallbacks middleware for API providers that do not support server-side fallbacks ([6b76649](https://github.com/anthropics/anthropic-sdk-python/commit/6b76649f99bd782d2300f2a6aa3f4a3f040af324)) ## 0.107.1 (2026-06-07) Full Changelog: [v0.107.0...v0.107.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.107.0...v0.107.1) ### Bug Fixes * **foundry:** send x-api-key header for API-key auth ([#62](https://github.com/anthropics/anthropic-sdk-python/issues/62)) ([1338141](https://github.com/anthropics/anthropic-sdk-python/commit/13381413d22ad14d85e66836c67cc8a13bd2b7bd)), closes [#1661](https://github.com/anthropics/anthropic-sdk-python/issues/1661) ## 0.107.0 (2026-06-06) Full Changelog: [v0.106.0...v0.107.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.106.0...v0.107.0) ### Features * **api:** small updates to Managed Agents types ([72923f9](https://github.com/anthropics/anthropic-sdk-python/commit/72923f986f808597f86482a7eae4fba9a791e6ae)) ## 0.106.0 (2026-06-05) Full Changelog: [v0.105.2...v0.106.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.105.2...v0.106.0) ### Features * **api:** mark Claude Opus 4.1 as deprecated ([85068cc](https://github.com/anthropics/anthropic-sdk-python/commit/85068cc4cb42feecb80a378942cec71e1baa8dcf)) ### Bug Fixes * **client:** make Foundry client copy() and with_options() work ([94146ac](https://github.com/anthropics/anthropic-sdk-python/commit/94146acdc1c6f66f187d5a42e4afbb911e692fe8)) * **transform schema:** preserve $defs when schema root is a $ref ([#1642](https://github.com/anthropics/anthropic-sdk-python/issues/1642)) ([fc58e06](https://github.com/anthropics/anthropic-sdk-python/commit/fc58e06b78407b447c50dfea109c6fb300f4b97d)) ### Chores * **internal:** fix artifact url ([a6ed0c4](https://github.com/anthropics/anthropic-sdk-python/commit/a6ed0c4124d29989a568a27293dadf66e7ebcd6f)) * **internal:** fix branch names ([3b03370](https://github.com/anthropics/anthropic-sdk-python/commit/3b0337074f0bbab47bf7f5a2b76b4d240cff719a)) * **internal:** update private repo name ([7dbcb05](https://github.com/anthropics/anthropic-sdk-python/commit/7dbcb05706f1865afeee62fb06e400f5c4bf619e)) ### Documentation * point security reports to Anthropic's HackerOne program ([#10](https://github.com/anthropics/anthropic-sdk-python/issues/10)) ([80f2c97](https://github.com/anthropics/anthropic-sdk-python/commit/80f2c97b8e9534f9879945de11c11aba00cf8704)) ## 0.105.2 (2026-05-29) Full Changelog: [v0.105.1...v0.105.2](https://github.com/anthropics/anthropic-sdk-python/compare/v0.105.1...v0.105.2) ## 0.105.1 (2026-05-29) Full Changelog: [v0.105.0...v0.105.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.105.0...v0.105.1) ### Chores * **internal:** use Trusted Publishing for PyPI releases ([1d04fc5](https://github.com/anthropics/anthropic-sdk-python/commit/1d04fc52d2dd1f88e22808de2c53b0d66913631f)) ## 0.105.0 (2026-05-28) Full Changelog: [v0.104.1...v0.105.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.104.1...v0.105.0) ### Features * **api:** Add support for claude-opus-4-8, mid-conversation system blocks, and usage.output_tokens_details ([f18b014](https://github.com/anthropics/anthropic-sdk-python/commit/f18b01414b21b49943a6ba2cdaa30ff7dd6a3025)) * support custom file size caps ([#1825](https://github.com/anthropics/anthropic-sdk-python/issues/1825)) ([7e5f944](https://github.com/anthropics/anthropic-sdk-python/commit/7e5f944ad85bd99526d9df30dc034f657472adaa)) ### Chores * **examples:** rename managed-agents private-sandbox-worker to self-hosted-sandbox-worker ([#1822](https://github.com/anthropics/anthropic-sdk-python/issues/1822)) ([750f956](https://github.com/anthropics/anthropic-sdk-python/commit/750f956a535b9e4772951d6bf1abd81203f27d4e)) ### Documentation * replace literal newlines ([8f7f6c0](https://github.com/anthropics/anthropic-sdk-python/commit/8f7f6c0d1b5ffb9563affdcf3dd2410dc72ed1b4)) ## 0.104.1 (2026-05-21) Full Changelog: [v0.104.0...v0.104.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.104.0...v0.104.1) ### Bug Fixes * **streaming:** carry encrypted_content through beta compaction accumulator ([#1821](https://github.com/anthropics/anthropic-sdk-python/issues/1821)) ([f7a720c](https://github.com/anthropics/anthropic-sdk-python/commit/f7a720c514cc5e428b310f46249ca1c807894c2e)) ## 0.104.0 (2026-05-21) Full Changelog: [v0.103.1...v0.104.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.103.1...v0.104.0) ### Features * **api:** Add support for thinking-token-count beta for estimated tokens in thinking block deltas when streaming ([80d0fdf](https://github.com/anthropics/anthropic-sdk-python/commit/80d0fdf460d6cd4f190681fd2241baf8ed76cc5f)) ## 0.103.1 (2026-05-19) Full Changelog: [v0.103.0...v0.103.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.103.0...v0.103.1) ### Bug Fixes * **runner:** skip tool calls SessionToolRunner does not own ([#1817](https://github.com/anthropics/anthropic-sdk-python/issues/1817)) ([9425c6a](https://github.com/anthropics/anthropic-sdk-python/commit/9425c6a0c6ff5e1d459ac081914f3df496365884)) ## 0.103.0 (2026-05-19) Full Changelog: [v0.102.0...v0.103.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.102.0...v0.103.0) ### Features * **client:** Add support for self-hosted sandboxes in CMA with sandbox helpers ([e5625b0](https://github.com/anthropics/anthropic-sdk-python/commit/e5625b0ae2a1e9d25847a53217c8fd70fa67c5ed)) ## 0.102.0 (2026-05-13) Full Changelog: [v0.101.0...v0.102.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.101.0...v0.102.0) ### Features * **api:** Add BetaManagedAgentsSearchResultBlock types ([3681f10](https://github.com/anthropics/anthropic-sdk-python/commit/3681f1042608b6be4e5a56e8b52e6a619b9210f4)) * **api:** Add support for cache diagnostics beta ([db51c6c](https://github.com/anthropics/anthropic-sdk-python/commit/db51c6caabb1bede4c2d671a96e6ef88e90ffaba)) * **internal/types:** support eagerly validating pydantic iterators ([68dabb0](https://github.com/anthropics/anthropic-sdk-python/commit/68dabb0e9b11ecd8745b231019e9bf788b72101a)) ### Chores * **api:** spec updates ([d579133](https://github.com/anthropics/anthropic-sdk-python/commit/d5791337776ba9d4876d3683e6ab9419365be9d3)) ## 0.101.0 (2026-05-11) Full Changelog: [v0.100.0...v0.101.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.100.0...v0.101.0) ### Features * **aws:** Add AWS client for Claude Platform on AWS ([1e70e3a](https://github.com/anthropics/anthropic-sdk-python/commit/1e70e3a21d57a96721685c1eca9cedd10cdd5b63)) ### Bug Fixes * **client:** add missing f-string prefix in file type error message ([06d109a](https://github.com/anthropics/anthropic-sdk-python/commit/06d109aaf36629ec15c8fb076c96aed722933600)) ### Chores * **examples:** bump tools_runner.py to claude-sonnet-4-5-20250929 ([#1473](https://github.com/anthropics/anthropic-sdk-python/issues/1473)) ([1aa8e41](https://github.com/anthropics/anthropic-sdk-python/commit/1aa8e410fd34d4c4971234a3ae7c7b11a5fadaf9)) * **examples:** update shebang from poetry to uv ([#1497](https://github.com/anthropics/anthropic-sdk-python/issues/1497)) ([ace8f38](https://github.com/anthropics/anthropic-sdk-python/commit/ace8f38dccd587efc0528aba14ec09b50480b514)) ## 0.100.0 (2026-05-06) Full Changelog: [v0.99.0...v0.100.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.99.0...v0.100.0) ### Features * **api:** add support for Managed Agents multiagents and outcomes, webhooks, vault validation ([3b3deee](https://github.com/anthropics/anthropic-sdk-python/commit/3b3deee9c479ce5b54411a8572b66c5a90f1d50f)) ### Bug Fixes * **api:** Adjust webhook configuration ([8c3339e](https://github.com/anthropics/anthropic-sdk-python/commit/8c3339e532458e93585f2faf4f284ccbb5829717)) ## 0.99.0 (2026-05-05) Full Changelog: [v0.98.1...v0.99.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.98.1...v0.99.0) ### Features * **client:** allow targeting a workspace for OIDC federation token exchange ([4ba8067](https://github.com/anthropics/anthropic-sdk-python/commit/4ba8067daa634691ea8c8a3b970d42bdaf5f04eb)) ## 0.98.1 (2026-05-04) Full Changelog: [v0.98.0...v0.98.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.98.0...v0.98.1) ### Chores * fix typo in example ([#1754](https://github.com/anthropics/anthropic-sdk-python/issues/1754)) ([de8ba13](https://github.com/anthropics/anthropic-sdk-python/commit/de8ba13769837f92ff00be8a1b1e9ad0749eae2f)) ## 0.98.0 (2026-05-04) Full Changelog: [v0.97.0...v0.98.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.97.0...v0.98.0) ### Features * **api:** improve Managed Agents APIs ([7faf393](https://github.com/anthropics/anthropic-sdk-python/commit/7faf3939a803420e7efd85cc18b67b97b429c172)) * **client:** add Workload Identity Federation, interactive OAuth, and auth profiles ([6458bcc](https://github.com/anthropics/anthropic-sdk-python/commit/6458bcc28e83adcd96cd084ed19ec113d5462c80)) * support setting headers via env ([52eb8cd](https://github.com/anthropics/anthropic-sdk-python/commit/52eb8cdd6e9a899519010d7e6ebc4a74a88f82cd)) ### Bug Fixes * **streaming:** propagate stop_details from message_delta onto accumulated Message ([#1725](https://github.com/anthropics/anthropic-sdk-python/issues/1725)) ([900dd9b](https://github.com/anthropics/anthropic-sdk-python/commit/900dd9b4376fd7a32d6e59d028b143558340d619)) * use correct field name format for multipart file arrays ([8350bdc](https://github.com/anthropics/anthropic-sdk-python/commit/8350bdced9599d023565c0cca93ff2d05560f991)) * **vertex:** async client missing us/eu multi-region base_url branches ([#1734](https://github.com/anthropics/anthropic-sdk-python/issues/1734)) ([3e78f71](https://github.com/anthropics/anthropic-sdk-python/commit/3e78f71c0ab3f3ff0e5402477cff06771c94864c)) ### Chores * **internal:** reformat pyproject.toml ([5a9d5fd](https://github.com/anthropics/anthropic-sdk-python/commit/5a9d5fd106c52643b87881d341b27dc7b12d5975)) ## 0.97.0 (2026-04-23) Full Changelog: [v0.96.0...v0.97.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.96.0...v0.97.0) ### Features * **api:** CMA Memory public beta ([fc30ebe](https://github.com/anthropics/anthropic-sdk-python/commit/fc30ebe5ca81204faa0b1d756b61dad176e37dcb)) ### Bug Fixes * **api:** fix errors in api spec ([f946de8](https://github.com/anthropics/anthropic-sdk-python/commit/f946de8da00748b472489e93ab4920d64d1cb22d)) * **api:** restore missing features ([72212ab](https://github.com/anthropics/anthropic-sdk-python/commit/72212ab8408af389981e9e6b111c00460b2b17e4)) ### Performance Improvements * **client:** optimize file structure copying in multipart requests ([1f9eed3](https://github.com/anthropics/anthropic-sdk-python/commit/1f9eed3a953c8cef0967df8470e04f7ac8fe3235)) ### Chores * add missing import ([4b12f5e](https://github.com/anthropics/anthropic-sdk-python/commit/4b12f5e0f4c29a234cd05f93c603b9cae2011aaa)) * **internal:** more robust bootstrap script ([7ed7370](https://github.com/anthropics/anthropic-sdk-python/commit/7ed737089d1f28385ee827f601ba81f1935d0b6a)) * **tests:** bump steady to v0.22.1 ([a4b7184](https://github.com/anthropics/anthropic-sdk-python/commit/a4b7184e57410ae92a409db5ee6fec90edceaa51)) ## 0.96.0 (2026-04-16) Full Changelog: [v0.95.0...v0.96.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.95.0...v0.96.0) ### Features * **api:** add claude-opus-4-7, token budgets and user_profiles ([0aa2a0d](https://github.com/anthropics/anthropic-sdk-python/commit/0aa2a0d4388a39984134d1dfc2bcbd6b206f7184)) ### Chores * **ci:** remove release-doctor workflow ([1d9add3](https://github.com/anthropics/anthropic-sdk-python/commit/1d9add35d0bd4c71f2bca3b0d494d1d0a348817a)) ## 0.95.0 (2026-04-14) Full Changelog: [v0.94.1...v0.95.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.94.1...v0.95.0) ### Features * **api:** mark Sonnet and Opus 4 as deprecated ([0c1e773](https://github.com/anthropics/anthropic-sdk-python/commit/0c1e7736394585dd021b53c1f87383c4fae29a6b)) * **bedrock:** use auth header for mantle client ([#1644](https://github.com/anthropics/anthropic-sdk-python/issues/1644)) ([3b93090](https://github.com/anthropics/anthropic-sdk-python/commit/3b93090e121861462f21a7621484cda66c139997)) ## 0.94.1 (2026-04-13) Full Changelog: [v0.94.0...v0.94.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.94.0...v0.94.1) ### Bug Fixes * **streaming:** add missing events ([c6a06d8](https://github.com/anthropics/anthropic-sdk-python/commit/c6a06d80b7e87bc034bd6ade950c735da02a0be3)) ## 0.94.0 (2026-04-10) Full Changelog: [v0.93.0...v0.94.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.93.0...v0.94.0) ### Features * vertex eu region ([#1658](https://github.com/anthropics/anthropic-sdk-python/issues/1658)) ([b7e157d](https://github.com/anthropics/anthropic-sdk-python/commit/b7e157d85f50b2900ddf896e8e80882dd7311bfd)) ### Bug Fixes * ensure file data are only sent as 1 parameter ([837b25b](https://github.com/anthropics/anthropic-sdk-python/commit/837b25bb6262186a5bae92aa70eb73c3cf8c90af)) ### Documentation * improve examples ([48089fd](https://github.com/anthropics/anthropic-sdk-python/commit/48089fdb788500d00718b9d4ae24cd34e5e91beb)) * update examples ([0f3c28b](https://github.com/anthropics/anthropic-sdk-python/commit/0f3c28b973026d135f91f38c4ad82ae2b1131522)) ## 0.93.0 (2026-04-09) Full Changelog: [v0.92.0...v0.93.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.92.0...v0.93.0) ### Features * **api:** Add beta advisor tool ([4297dca](https://github.com/anthropics/anthropic-sdk-python/commit/4297dca285441b185ea9e3d18b7f912102b54be2)) ## 0.92.0 (2026-04-08) Full Changelog: [v0.91.0...v0.92.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.91.0...v0.92.0) ### Features * **api:** add support for Claude Managed Agents ([5b879a7](https://github.com/anthropics/anthropic-sdk-python/commit/5b879a7d929bd93332d777bed067be680819dfac)) ## 0.91.0 (2026-04-07) Full Changelog: [v0.90.0...v0.91.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.90.0...v0.91.0) ### Features * **client:** Create Bedrock Mantle client ([#1616](https://github.com/anthropics/anthropic-sdk-python/issues/1616)) ([fd195a2](https://github.com/anthropics/anthropic-sdk-python/commit/fd195a2fa2cd44ebf4513e69f671def88d2b6ec9)) ## 0.90.0 (2026-04-07) Full Changelog: [v0.89.0...v0.90.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.89.0...v0.90.0) ### Features * **api:** Add support for claude-mythos-preview ([fc7ddd8](https://github.com/anthropics/anthropic-sdk-python/commit/fc7ddd8e0296a578f09c7fa2baf00e50d81cf980)) ### Bug Fixes * **client:** preserve hardcoded query params when merging with user params ([32d35e0](https://github.com/anthropics/anthropic-sdk-python/commit/32d35e0ae67ab0d076a60d38fa5177b5635e9c0c)) ## 0.89.0 (2026-04-03) Full Changelog: [v0.88.0...v0.89.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.88.0...v0.89.0) ### Features * **vertex:** add support for US multi-region endpoint ([4e732da](https://github.com/anthropics/anthropic-sdk-python/commit/4e732dada087146cfeff1f4afdf90513590e248d)) ### Bug Fixes * **client:** preserve hardcoded query params when merging with user params ([e7f4a3c](https://github.com/anthropics/anthropic-sdk-python/commit/e7f4a3cada266e9719e5c3b9ba09514c3842a638)) ### Chores * **client:** deprecate client-side compaction helpers ([e60affc](https://github.com/anthropics/anthropic-sdk-python/commit/e60affc656e4165de7cb15f73351175507b0b441)) ## 0.88.0 (2026-04-01) Full Changelog: [v0.87.0...v0.88.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.87.0...v0.88.0) ### Features * **api:** add structured stop_details to message responses ([fd82d6b](https://github.com/anthropics/anthropic-sdk-python/commit/fd82d6b87ef0db5b2970d8f27ccc6d5981745572)) * bedrock api key auth ([#1623](https://github.com/anthropics/anthropic-sdk-python/issues/1623)) ([a95a3fc](https://github.com/anthropics/anthropic-sdk-python/commit/a95a3fc586b8de63e3c2b386cee5e312d96bf5d8)) * prepare aws package ([#1615](https://github.com/anthropics/anthropic-sdk-python/issues/1615)) ([6875fab](https://github.com/anthropics/anthropic-sdk-python/commit/6875fab38ac27ab3a09b97088a49925abe011bdc)) ### Chores * **tests:** bump steady to v0.20.2 ([1bc4e9f](https://github.com/anthropics/anthropic-sdk-python/commit/1bc4e9ffc160eb1ded6294652936caafd6dfc64a)) ## 0.87.0 (2026-03-31) Full Changelog: [v0.86.0...v0.87.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.86.0...v0.87.0) ### Features * **client:** add error type field to APIStatusError ([#1587](https://github.com/anthropics/anthropic-sdk-python/issues/1587)) ([dd563c0](https://github.com/anthropics/anthropic-sdk-python/commit/dd563c031c2a0be75ccb6175246685abd5806d7d)) * **internal:** implement indices array format for query and form serialization ([11a6244](https://github.com/anthropics/anthropic-sdk-python/commit/11a624467bd44175bc602f0135ff354895bdebdd)) ### Bug Fixes * honor __api_exclude__ in async transform path ([#1612](https://github.com/anthropics/anthropic-sdk-python/issues/1612)) ([8172232](https://github.com/anthropics/anthropic-sdk-python/commit/8172232a8bb19e0d0bf10df1c3c21ed492784585)), closes [#1610](https://github.com/anthropics/anthropic-sdk-python/issues/1610) * **memory:** return resolved path from async _validate_path ([7b0add3](https://github.com/anthropics/anthropic-sdk-python/commit/7b0add32bd5fc59ad0fa277ef6982ee1df1eed7a)) * **memory:** use restrictive file mode for memory files ([47ba5b8](https://github.com/anthropics/anthropic-sdk-python/commit/47ba5b8f5f74beb1e1babef249754e1312b9dddf)) * sanitize endpoint path params ([98f60e4](https://github.com/anthropics/anthropic-sdk-python/commit/98f60e42039392a133d83c8673d659f514c15a35)) * **transform schema:** support enums ([#1275](https://github.com/anthropics/anthropic-sdk-python/issues/1275)) ([5c088ab](https://github.com/anthropics/anthropic-sdk-python/commit/5c088ab1d162b1c1a18f566688b31bfbd7610825)) ### Chores * **ci:** run builds on CI even if only spec metadata changed ([194c050](https://github.com/anthropics/anthropic-sdk-python/commit/194c05029403cef820897c3c6b2c26d4df0736f7)) * **ci:** skip lint on metadata-only changes ([03e2ab9](https://github.com/anthropics/anthropic-sdk-python/commit/03e2ab9e95ec452d7d519e0b419c8881f3ae3a08)) * **internal:** update gitignore ([94ede14](https://github.com/anthropics/anthropic-sdk-python/commit/94ede14b443c78b51931c185d1cd44f4ef201eae)) * **tests:** bump steady to v0.19.4 ([2d6d58f](https://github.com/anthropics/anthropic-sdk-python/commit/2d6d58fa0101930c8f5cd9e9a94e7e988055f371)) * **tests:** bump steady to v0.19.5 ([8fb439a](https://github.com/anthropics/anthropic-sdk-python/commit/8fb439afeadaf608cbf7d4630d01735f97227e3e)) * **tests:** bump steady to v0.19.6 ([76da5fd](https://github.com/anthropics/anthropic-sdk-python/commit/76da5fdd03b7ffc65a8b58b9f2a0df3e03c587c9)) * **tests:** bump steady to v0.19.7 ([bfa40e5](https://github.com/anthropics/anthropic-sdk-python/commit/bfa40e5c5bed65da0f3f664082e58e85c26b9c66)) * **tests:** bump steady to v0.20.1 ([4fd9446](https://github.com/anthropics/anthropic-sdk-python/commit/4fd9446332ae114072dac968134e6451c62138bb)) ## 0.86.0 (2026-03-18) Full Changelog: [v0.85.0...v0.86.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.85.0...v0.86.0) ### Features * add support for filesystem memory tools ([#1247](https://github.com/anthropics/anthropic-sdk-python/issues/1247)) ([235d218](https://github.com/anthropics/anthropic-sdk-python/commit/235d218211ac4b8f1aa37e29bedc998bfb6ce77d)) * **api:** manual updates ([86dbe4a](https://github.com/anthropics/anthropic-sdk-python/commit/86dbe4aa58386bfb8d1497debf342e929e9bb5e5)) * **api:** manual updates ([45d9cc0](https://github.com/anthropics/anthropic-sdk-python/commit/45d9cc0914200a43743ab11aa311392e9d8c1b4f)) ### Bug Fixes * AsyncAnthropic._make_status_error missing 529 and 413 cases ([#1244](https://github.com/anthropics/anthropic-sdk-python/issues/1244)) ([05220bc](https://github.com/anthropics/anthropic-sdk-python/commit/05220bc1c1079fe01f5c4babc007ec7a990859d9)) * **deps:** bump minimum typing-extensions version ([09ab112](https://github.com/anthropics/anthropic-sdk-python/commit/09ab112289815ba6f19d8fb3da1e715748182799)) * **pydantic:** do not pass `by_alias` unless set ([b17480e](https://github.com/anthropics/anthropic-sdk-python/commit/b17480e9d06613aa597dd40d5a47f4f1250ac762)) ### Chores * **internal:** tweak CI branches ([3c0308c](https://github.com/anthropics/anthropic-sdk-python/commit/3c0308c97804ababfd3f37330e129e68ccfe4bbc)) ## 0.85.0 (2026-03-16) Full Changelog: [v0.84.0...v0.85.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.84.0...v0.85.0) ### Features * **api:** chore(config): clean up model enum list ([#31](https://github.com/anthropics/anthropic-sdk-python/issues/31)) ([cce1a5b](https://github.com/anthropics/anthropic-sdk-python/commit/cce1a5b9e6fce4f269cec42803f37ce5e2ac2f76)) * **api:** GA thinking-display-setting ([207340c](https://github.com/anthropics/anthropic-sdk-python/commit/207340cc621855928f53e8ddd58f216ac0d8150d)) * **tests:** update mock server ([7dc86a4](https://github.com/anthropics/anthropic-sdk-python/commit/7dc86a4ffc9e70533a58065496c78394c6a6e97a)) ### Bug Fixes * **client:** add missing 413 and 529 error handlers to async client ([#1554](https://github.com/anthropics/anthropic-sdk-python/issues/1554)) ([9c2986f](https://github.com/anthropics/anthropic-sdk-python/commit/9c2986fb9c046b4cffa1b03ca8762f9c9dea0bab)) * **tool runner:** propagate container_id for programmatic tool calling ([#1462](https://github.com/anthropics/anthropic-sdk-python/issues/1462)) ([3ae7ff6](https://github.com/anthropics/anthropic-sdk-python/commit/3ae7ff6ff7af8a881706ae8068b1040a23c96fbd)) * **tools:** use filtered messages list in async compaction ([#1124](https://github.com/anthropics/anthropic-sdk-python/issues/1124)) ([710d666](https://github.com/anthropics/anthropic-sdk-python/commit/710d666f80b7667e3551c1a68d7c0ffaad115de1)) ### Chores * **ci:** bump uv version ([09656ac](https://github.com/anthropics/anthropic-sdk-python/commit/09656acef77fa459d30d811bd51aa780a567182b)) * **internal:** codegen related update ([c9e9fc2](https://github.com/anthropics/anthropic-sdk-python/commit/c9e9fc240334fc466426646d7acd64904f881a80)) * **internal:** codegen related update ([77f77d1](https://github.com/anthropics/anthropic-sdk-python/commit/77f77d19b4657a7ad0d31de42504c25cf4ed76ef)) * **tests:** unskip tests that are now supported in steady ([827330b](https://github.com/anthropics/anthropic-sdk-python/commit/827330b527b4af299af084752a7317b0596956af)) ## 0.84.0 (2026-02-25) Full Changelog: [v0.83.0...v0.84.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.83.0...v0.84.0) ### Features * **api:** change array_format to brackets ([925d2ad](https://github.com/anthropics/anthropic-sdk-python/commit/925d2ad6b76ad7c15de07b9b2768738775f71631)) * **api:** remove publishing section from cli target ([7bc7ceb](https://github.com/anthropics/anthropic-sdk-python/commit/7bc7cebc68db70f08fce23e7e0b24acbc9ff37a7)) * **helpers:** add conversion helpers for MCP tools, prompts, and resources ([#1383](https://github.com/anthropics/anthropic-sdk-python/issues/1383)) ([9489751](https://github.com/anthropics/anthropic-sdk-python/commit/9489751386d1540bf80eff63ab47ca2b3cc18fa1)) ### Chores * add missing raw jsonl results method ([1009d4a](https://github.com/anthropics/anthropic-sdk-python/commit/1009d4aca8be42973ca39104bc9bd8087f51ff9c)) * **internal:** add request options to SSE classes ([4f4bc8e](https://github.com/anthropics/anthropic-sdk-python/commit/4f4bc8e6241c2ccee8dfe4cdbc522081e3e30f08)) * **internal:** make `test_proxy_environment_variables` more resilient ([f7056e0](https://github.com/anthropics/anthropic-sdk-python/commit/f7056e09411a45798a678be5766a7b7d6dcbc7a9)) * **internal:** make `test_proxy_environment_variables` more resilient to env ([143efcc](https://github.com/anthropics/anthropic-sdk-python/commit/143efccfcc20c12f920b6ba242eff7c0feeea7c4)) * **internal:** simplify http snapshots ([#1092](https://github.com/anthropics/anthropic-sdk-python/issues/1092)) ([4a4dc9f](https://github.com/anthropics/anthropic-sdk-python/commit/4a4dc9f6b36ab0224095790f4311c7f60c9845f7)) * **internal:** update jsonl tests ([a8e6a6e](https://github.com/anthropics/anthropic-sdk-python/commit/a8e6a6e5544b9f1626e3fb5faa31a1accfc81441)) ### Documentation * rebrand to Claude SDK and streamline README ([6b54405](https://github.com/anthropics/anthropic-sdk-python/commit/6b544058ab19e55e1c76a4ba9816205d1eedc630)) ## 0.83.0 (2026-02-19) Full Changelog: [v0.82.0...v0.83.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.82.0...v0.83.0) ### Features * **api:** Add top-level cache control (automatic caching) ([a940123](https://github.com/anthropics/anthropic-sdk-python/commit/a940123da34ac33f0b6f20ce91807829451d1233)) ### Chores * update mock server docs ([34ef48c](https://github.com/anthropics/anthropic-sdk-python/commit/34ef48ceb0f1734d6b695890f689dc42eb0b004e)) ## 0.82.0 (2026-02-18) Full Changelog: [v0.81.0...v0.82.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.81.0...v0.82.0) ### Features * **api:** fix shared UserLocation and error code types ([da3b931](https://github.com/anthropics/anthropic-sdk-python/commit/da3b931a2be768d77c228a4804d2f7f75caeb71c)) ### Bug Fixes * add backward-compat aliases for removed nested UserLocation classes ([#1409](https://github.com/anthropics/anthropic-sdk-python/issues/1409)) ([56db1e3](https://github.com/anthropics/anthropic-sdk-python/commit/56db1e3db6108e1c0f4e9363a5f23b54976dc877)) ## 0.81.0 (2026-02-18) Full Changelog: [v0.80.0...v0.81.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.80.0...v0.81.0) ### Features * **api:** manual updates ([0a385c2](https://github.com/anthropics/anthropic-sdk-python/commit/0a385c29d26981f846b7394aefc89eebb43a4b60)) ## 0.80.0 (2026-02-17) Full Changelog: [v0.79.0...v0.80.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.79.0...v0.80.0) ### Features * **api:** Releasing claude-sonnet-4-6 ([d518d6e](https://github.com/anthropics/anthropic-sdk-python/commit/d518d6ecede3d0638f0b14950dc2be8efa0b4ff4)) ### Bug Fixes * **api:** fix spec errors ([1413a76](https://github.com/anthropics/anthropic-sdk-python/commit/1413a76f905e590fab583417f5cb1eef9f537c2c)) * remove speed from ga messages ([#1402](https://github.com/anthropics/anthropic-sdk-python/issues/1402)) ([f6ce67c](https://github.com/anthropics/anthropic-sdk-python/commit/f6ce67c3ed5f2fc4a2fc48fb9d7bc6f1bbb5bd4a)) ### Chores * format all `api.md` files ([28a0eb5](https://github.com/anthropics/anthropic-sdk-python/commit/28a0eb55c031a9ed584eafe7f9096b32f9883e6f)) * **internal:** bump dependencies ([99f3014](https://github.com/anthropics/anthropic-sdk-python/commit/99f301460a3933229768d19fa7ae725072012592)) * **internal:** fix lint error on Python 3.14 ([a90d71b](https://github.com/anthropics/anthropic-sdk-python/commit/a90d71bfcdef5592f0f7f9a176cf347163ee2137)) ### Refactors * **vertex:** remove redundant isinstance check in `load_auth` ([#1387](https://github.com/anthropics/anthropic-sdk-python/issues/1387)) ([6b7a7dc](https://github.com/anthropics/anthropic-sdk-python/commit/6b7a7dce065b7bfbf6c5d8ed41825f36b36fc402)) ## 0.79.0 (2026-02-07) Full Changelog: [v0.78.0...v0.79.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.78.0...v0.79.0) ### Features * **api:** enabling fast-mode in claude-opus-4-6 ([5953ba7](https://github.com/anthropics/anthropic-sdk-python/commit/5953ba7b425ba113595de570bc8c639ff4dc4047)) ### Bug Fixes * pass speed parameter through in sync beta count_tokens ([1dd6119](https://github.com/anthropics/anthropic-sdk-python/commit/1dd6119daca6de7a6eb730eb2494f368889ea050)) ## 0.78.0 (2026-02-05) Full Changelog: [v0.77.1...v0.78.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.77.1...v0.78.0) ### Features * **api:** Release Claude Opus 4.6, adaptive thinking, and other features ([3ef1529](https://github.com/anthropics/anthropic-sdk-python/commit/3ef1529b45c55645646cc6043784f999fda088de)) ## 0.77.1 (2026-02-03) Full Changelog: [v0.77.0...v0.77.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.77.0...v0.77.1) ### Bug Fixes * **structured outputs:** send structured output beta header when format is omitted ([#1158](https://github.com/anthropics/anthropic-sdk-python/issues/1158)) ([258494e](https://github.com/anthropics/anthropic-sdk-python/commit/258494e2b814a6a096b01e50f83560b4cf4a98ad)) ### Chores * remove claude-code-review workflow ([#1338](https://github.com/anthropics/anthropic-sdk-python/issues/1338)) ([aec4512](https://github.com/anthropics/anthropic-sdk-python/commit/aec4512305e8dce41df8ef0ab225f4939e099bcf)) ## 0.77.0 (2026-01-29) Full Changelog: [v0.76.0...v0.77.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.76.0...v0.77.0) ### Features * **api:** add support for Structured Outputs in the Messages API ([ad56677](https://github.com/anthropics/anthropic-sdk-python/commit/ad5667774ad2e7efd181bcfda03fab3ea50630b9)) * **api:** migrate sending message format in output_config rather than output_format ([af405e4](https://github.com/anthropics/anthropic-sdk-python/commit/af405e473f7cf6091cb8e711264227b9b0508528)) * **client:** add custom JSON encoder for extended type support ([7780e90](https://github.com/anthropics/anthropic-sdk-python/commit/7780e90bd2fe4c1116d59bc0ad543aa609fc643d)) * use output_config for structured outputs ([82d669d](https://github.com/anthropics/anthropic-sdk-python/commit/82d669db652ed3d9aede61fd500fabb291b8f035)) ### Bug Fixes * **client:** run formatter ([2e4ff86](https://github.com/anthropics/anthropic-sdk-python/commit/2e4ff86d7b8bef8fe5c4b7e62bf47dfff79f0577)) * remove class causing breaking change ([#1333](https://github.com/anthropics/anthropic-sdk-python/issues/1333)) ([81ee953](https://github.com/anthropics/anthropic-sdk-python/commit/81ee9533d14f9dc3753a4a1320ea744825b17e92)) * **structured outputs:** avoid including beta header if `output_format` is missing ([#1121](https://github.com/anthropics/anthropic-sdk-python/issues/1121)) ([062077e](https://github.com/anthropics/anthropic-sdk-python/commit/062077e50d182719637403576f59761999b3b2f5)) ### Chores * **ci:** upgrade `actions/github-script` ([34df616](https://github.com/anthropics/anthropic-sdk-python/commit/34df6160ad386a7e8848e3435b22bd18bd726702)) * **internal:** update `actions/checkout` version ([ea50de9](https://github.com/anthropics/anthropic-sdk-python/commit/ea50de95bd1e43b8f00a45ef472330a3c8b396c8)) ## 0.76.0 (2026-01-13) Full Changelog: [v0.75.0...v0.76.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.75.0...v0.76.0) ### Features * allow raw JSON schema to be passed to messages.stream() ([955c61d](https://github.com/anthropics/anthropic-sdk-python/commit/955c61dd5aae4c8a2c7b8fab1f97a0b88c0ef03b)) * **client:** add support for binary request streaming ([5302f27](https://github.com/anthropics/anthropic-sdk-python/commit/5302f2724c9890340c1b0dd042a1e670ed00eb93)) * **tool runner:** add support for server-side tools ([#1086](https://github.com/anthropics/anthropic-sdk-python/issues/1086)) ([1521316](https://github.com/anthropics/anthropic-sdk-python/commit/15213160a016a70538c81163c49ce5948fe06879)) ### Bug Fixes * **client:** loosen auth header validation ([5a0b89b](https://github.com/anthropics/anthropic-sdk-python/commit/5a0b89bb2c808cd0a413697a1141d4835ce00181)) * ensure streams are always closed ([388bd0c](https://github.com/anthropics/anthropic-sdk-python/commit/388bd0cbc53c4d8d8884d17a3051623728588eb4)) * **types:** allow pyright to infer TypedDict types within SequenceNotStr ([ede3242](https://github.com/anthropics/anthropic-sdk-python/commit/ede32426043273f9b31e70893207ad6519240591)) * use async_to_httpx_files in patch method ([718fa8e](https://github.com/anthropics/anthropic-sdk-python/commit/718fa8e62aa939dd8c5d46430aa1d1b05a5906d9)) ### Chores * add missing docstrings ([d306605](https://github.com/anthropics/anthropic-sdk-python/commit/d306605103649320e900ab3a2413d0dbd6b118c5)) * bump required `uv` version ([90634f3](https://github.com/anthropics/anthropic-sdk-python/commit/90634f3ef0a9d7ae5a1945f005b13aad245f6b32)) * **ci:** Add Claude Code GitHub Workflow ([#1293](https://github.com/anthropics/anthropic-sdk-python/issues/1293)) ([83d1c4a](https://github.com/anthropics/anthropic-sdk-python/commit/83d1c4aef1ae34b1aebe4ca25de8b0cd2d37a493)) * **deps:** mypy 1.18.1 has a regression, pin to 1.17 ([21c6374](https://github.com/anthropics/anthropic-sdk-python/commit/21c6374f3825f43e104bad4a5df71941bcf09844)) * **docs:** use environment variables for authentication in code snippets ([87aa378](https://github.com/anthropics/anthropic-sdk-python/commit/87aa378f13f64099bec9513cba85ba9723773ec4)) * fix docstring ([51fca79](https://github.com/anthropics/anthropic-sdk-python/commit/51fca7942b4e74e1357fb72828e8e39a8b8eea6a)) * **internal:** add `--fix` argument to lint script ([8914b7a](https://github.com/anthropics/anthropic-sdk-python/commit/8914b7abe9b98565f08f4965f7fd0da8cd9f1f08)) * **internal:** add missing files argument to base client ([6285abc](https://github.com/anthropics/anthropic-sdk-python/commit/6285abcba9945a7c6c6b713940ee0478dfe25008)) * **internal:** avoid using unstable Python versions in tests ([4547171](https://github.com/anthropics/anthropic-sdk-python/commit/4547171aba17e41ff2f2e2c13d319b6bc1a13e85)) * update lockfile ([d7ae1fc](https://github.com/anthropics/anthropic-sdk-python/commit/d7ae1fc9c06d7565b909e5c2d48ebeb63ee9d8c9)) * update uv.lock ([746ac05](https://github.com/anthropics/anthropic-sdk-python/commit/746ac05cbb18c7d596a381e2fe89d0ee3e4e94b9)) ## 0.75.0 (2025-11-24) Full Changelog: [v0.74.1...v0.75.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.74.1...v0.75.0) ### Features * **api:** adds support for Claude Opus 4.5, Effort, Advance Tool Use Features, Autocompaction, and Computer Use v5 ([5c3e633](https://github.com/anthropics/anthropic-sdk-python/commit/5c3e633395acc100c45e8a403f1b5be4d17a3b32)) ### Bug Fixes * **internal:** small fixes ([36c82f7](https://github.com/anthropics/anthropic-sdk-python/commit/36c82f72b588bd549770a89072dd52d6f9e5b6a5)) ### Chores * fix lint issues ([4f1fd54](https://github.com/anthropics/anthropic-sdk-python/commit/4f1fd54143b79a4a7976dfb332ec7bfc666d1598)) ## 0.74.1 (2025-11-19) Full Changelog: [v0.74.0...v0.74.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.74.0...v0.74.1) ### Bug Fixes * **structured outputs:** use correct beta header ([e90d347](https://github.com/anthropics/anthropic-sdk-python/commit/e90d347dfd80adc5ae2a412ebfe2eb55351ce08e)) ### Chores * **examples:** update model references ([e09461d](https://github.com/anthropics/anthropic-sdk-python/commit/e09461da36405bbb1a7ef616b9281457b2a6e20f)) ## 0.74.0 (2025-11-18) Full Changelog: [v0.73.0...v0.74.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.73.0...v0.74.0) ### Features * add Foundry SDK ([3ae9e45](https://github.com/anthropics/anthropic-sdk-python/commit/3ae9e45451d3ff85b25ba5f5f9f8786ea35e3cc9)) ### Bug Fixes * **examples/memory:** properly add assistant_content to messages ([#1049](https://github.com/anthropics/anthropic-sdk-python/issues/1049)) ([9c7141b](https://github.com/anthropics/anthropic-sdk-python/commit/9c7141b887bbde251b6b740405a91d1726308c32)) * use posix paths in file collection for cross-platform compatibility ([d9c6f40](https://github.com/anthropics/anthropic-sdk-python/commit/d9c6f4006a43fe094acbf652a8dbd4234853ed70)), closes [#1051](https://github.com/anthropics/anthropic-sdk-python/issues/1051) ### Chores * **internal:** remove unnecessary wrapper around external snapshots ([19eceac](https://github.com/anthropics/anthropic-sdk-python/commit/19eceac2406e4c71db14f9e870aaa901b4249219)) ### Documentation * explain snapshot update process ([#1040](https://github.com/anthropics/anthropic-sdk-python/issues/1040)) ([b61fd87](https://github.com/anthropics/anthropic-sdk-python/commit/b61fd87986a90105c20d4f35e34a9f69ec73645f)) ## 0.73.0 (2025-11-14) Full Changelog: [v0.72.1...v0.73.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.72.1...v0.73.0) ### Features * **api:** add support for structured outputs beta ([688da81](https://github.com/anthropics/anthropic-sdk-python/commit/688da8126df8a304c5f01f0dc63cda437a62c217)) ## 0.72.1 (2025-11-11) Full Changelog: [v0.72.0...v0.72.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.72.0...v0.72.1) ### Bug Fixes * **client:** close streams without requiring full consumption ([109b771](https://github.com/anthropics/anthropic-sdk-python/commit/109b77175c844c37e6e57899a732a0ba293a2942)) * compat with Python 3.14 ([bd2a137](https://github.com/anthropics/anthropic-sdk-python/commit/bd2a137a46cd899e28e81dd8d445dad23440674c)) * **compat:** update signatures of `model_dump` and `model_dump_json` for Pydantic v1 ([540f0f8](https://github.com/anthropics/anthropic-sdk-python/commit/540f0f8fdb0aa5d0d104259a59a0ee359296953e)) ### Chores * **internal/tests:** avoid race condition with implicit client cleanup ([72767ce](https://github.com/anthropics/anthropic-sdk-python/commit/72767cebc31a9d5608f08533888a1cf69862a598)) * **internal:** grammar fix (it's -> its) ([9efe993](https://github.com/anthropics/anthropic-sdk-python/commit/9efe99371577c3ff51050aeab5d480a442577b46)) * **package:** drop Python 3.8 support ([e9af5d3](https://github.com/anthropics/anthropic-sdk-python/commit/e9af5d3676a0a77881a18bc9b4e8a30ecf46b017)) ## 0.72.0 (2025-10-28) Full Changelog: [v0.71.1...v0.72.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.71.1...v0.72.0) ### Features * **api:** add ability to clear thinking in context management ([27c8f17](https://github.com/anthropics/anthropic-sdk-python/commit/27c8f17c573c73c4db2146731ef1ab712140b0a2)) ## 0.71.1 (2025-10-28) Full Changelog: [v0.71.0...v0.71.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.71.0...v0.71.1) ### Bug Fixes * **client:** resolve non-functional default socket options ([4606137](https://github.com/anthropics/anthropic-sdk-python/commit/4606137fcca27ab2d03669999b624c11394b090a)) ### Chores * **api:** mark older sonnet models as deprecated ([7906595](https://github.com/anthropics/anthropic-sdk-python/commit/7906595fe2f214cf0449d073145629ea8d3da437)) * bump `httpx-aiohttp` version to 0.1.9 ([5d27492](https://github.com/anthropics/anthropic-sdk-python/commit/5d2749222bb75201279c1877690c75687f3f8abc)) ## 0.71.0 (2025-10-16) Full Changelog: [v0.70.0...v0.71.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.70.0...v0.71.0) ### Features * **api:** adding support for agent skills ([51a606f](https://github.com/anthropics/anthropic-sdk-python/commit/51a606f497fd278c96af00936aa98d94bdfc9ae4)) ## 0.70.0 (2025-10-15) Full Changelog: [v0.69.0...v0.70.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.69.0...v0.70.0) ### Features * **api:** manual updates ([39e62ac](https://github.com/anthropics/anthropic-sdk-python/commit/39e62ac0de90c09dc37aa5e3b44f2c884268e042)) ### Chores * **client:** add context-management-2025-06-27 beta header ([36dd334](https://github.com/anthropics/anthropic-sdk-python/commit/36dd3346b1460f003270888693fbc82dba16dc62)) * **client:** add model-context-window-exceeded-2025-08-26 beta header ([2cbdb0f](https://github.com/anthropics/anthropic-sdk-python/commit/2cbdb0ff6342550def540ce6081af78823f9e60b)) * **internal:** detect missing future annotations with ruff ([b2a2b05](https://github.com/anthropics/anthropic-sdk-python/commit/b2a2b05868b470478fccee819432e844bc37eec9)) ## 0.69.0 (2025-09-29) Full Changelog: [v0.68.2...v0.69.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.68.2...v0.69.0) ### Features * **api:** adds support for Claude Sonnet 4.5 and context management features ([f93eb12](https://github.com/anthropics/anthropic-sdk-python/commit/f93eb12dbfeaa68fb24590391ec72243836eb47a)) ## 0.68.2 (2025-09-29) Full Changelog: [v0.68.1...v0.68.2](https://github.com/anthropics/anthropic-sdk-python/compare/v0.68.1...v0.68.2) ### Bug Fixes * do not set headers with default to omit ([95b14ab](https://github.com/anthropics/anthropic-sdk-python/commit/95b14ab8fa8b63b95cb82cad1347915c163818d2)) ## 0.68.1 (2025-09-26) Full Changelog: [v0.68.0...v0.68.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.68.0...v0.68.1) ### Chores * **deps:** move deprecated `dev-dependencies` in `pyproject.toml` to dev group ([df16b88](https://github.com/anthropics/anthropic-sdk-python/commit/df16b88ade25f91c0b2c88d4ae256c861e4db170)) * do not install brew dependencies in ./scripts/bootstrap by default ([a457673](https://github.com/anthropics/anthropic-sdk-python/commit/a45767347f27258290c572ee5dc3e44c4f314f4b)) * rename tool runner helper header ([a9ed3f9](https://github.com/anthropics/anthropic-sdk-python/commit/a9ed3f9c80dc1acbf7f487fc6cc3586f690ccd2d)) * **types:** change optional parameter type from NotGiven to Omit ([9f0a11f](https://github.com/anthropics/anthropic-sdk-python/commit/9f0a11fa6168e5d59f8a5820de5dbe7d5c2137bd)) * update more NotGiven usage sites ([72ab661](https://github.com/anthropics/anthropic-sdk-python/commit/72ab661827ead771923d229bd142f3b9c1a234e8)) ## 0.68.0 (2025-09-17) Full Changelog: [v0.67.0...v0.68.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.67.0...v0.68.0) ### Features * add tool running helpers ([d9c9ce6](https://github.com/anthropics/anthropic-sdk-python/commit/d9c9ce63de5b5888dff0d9ba2d79724520152420)) ### Chores * **internal:** fix tests ([9858c79](https://github.com/anthropics/anthropic-sdk-python/commit/9858c791309a1af2fbc2cb4d042812de83d1a635)) * **internal:** update pydantic dependency ([f59c2f1](https://github.com/anthropics/anthropic-sdk-python/commit/f59c2f196a09c80f6b46cac19e9d0ace18200da8)) * **tests:** simplify `get_platform` test ([7596748](https://github.com/anthropics/anthropic-sdk-python/commit/7596748699ff3113b6cb6c1511cf4a33d0453233)) ## 0.67.0 (2025-09-10) Full Changelog: [v0.66.0...v0.67.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.66.0...v0.67.0) ### Features * **api:** adds support for web_fetch_20250910 tool ([f85b6a1](https://github.com/anthropics/anthropic-sdk-python/commit/f85b6a172b4b6d3b42119d75f0fefbbf7dcd2351)) * improve future compat with pydantic v3 ([39f28c5](https://github.com/anthropics/anthropic-sdk-python/commit/39f28c51d328bc7bf5f86f252ea63e8325815c72)) ### Bug Fixes * more updates for future pydantic v3 compat ([7967d15](https://github.com/anthropics/anthropic-sdk-python/commit/7967d1501297e188f451fa39a33b38317dfcb883)) * **types/beta:** add response content block type to params ([#1030](https://github.com/anthropics/anthropic-sdk-python/issues/1030)) ([9febe38](https://github.com/anthropics/anthropic-sdk-python/commit/9febe38a309d290f2c2a8d96eb458972652b98cd)) ### Chores * **internal:** move mypy configurations to `pyproject.toml` file ([c5347b6](https://github.com/anthropics/anthropic-sdk-python/commit/c5347b6affba397276c13e763f5eb820d11f912f)) * update SDK settings ([36e6870](https://github.com/anthropics/anthropic-sdk-python/commit/36e687098acc8c66613712c89667edea73ce799f)) ## 0.66.0 (2025-09-03) Full Changelog: [v0.65.0...v0.66.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.65.0...v0.66.0) ### Features * **api:** adds support for Documents in tool results ([5309dad](https://github.com/anthropics/anthropic-sdk-python/commit/5309dad584bb31284516e0d44681f1b46d1a663d)) ## 0.65.0 (2025-09-02) Full Changelog: [v0.64.0...v0.65.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.64.0...v0.65.0) ### Features * **client:** adds support for code-execution-2025-08-26 tool ([fe92af0](https://github.com/anthropics/anthropic-sdk-python/commit/fe92af02a2fa2d1bc626bb238ee54886bc701829)) * **types:** replace List[str] with SequenceNotStr in params ([f542b54](https://github.com/anthropics/anthropic-sdk-python/commit/f542b541ba1e70b65704e9b96a87733cb1d9d77f)) ### Bug Fixes * avoid newer type syntax ([c6d1cf5](https://github.com/anthropics/anthropic-sdk-python/commit/c6d1cf5895f15d5fc526af9a34a6b53a514bf28e)) * **client:** remove unused import ([712c6d8](https://github.com/anthropics/anthropic-sdk-python/commit/712c6d8b1c47eb663d2fc06b97b8c0630d4579cf)) ### Chores * **client:** sync SequenceNotStr over to custom stream methods ([dd16483](https://github.com/anthropics/anthropic-sdk-python/commit/dd16483da5900a9c02519617d52d951f429d4685)) * **internal:** add Sequence related utils ([d523f29](https://github.com/anthropics/anthropic-sdk-python/commit/d523f295541c23de43d3b51512327265d243ac35)) * **internal:** bump uv version ([aab5bc6](https://github.com/anthropics/anthropic-sdk-python/commit/aab5bc667e5aa1144dc45650cd4c6a63f1be6051)) * **internal:** change ci workflow machines ([5383431](https://github.com/anthropics/anthropic-sdk-python/commit/5383431a31c7368c45f7cc42b358798a2f2c8a7f)) * **internal:** codegen related update ([eb8b19f](https://github.com/anthropics/anthropic-sdk-python/commit/eb8b19f5d7e6e4af3cfbf4cc6f02a9e5ce50f1b8)) * **internal:** improve breaking change detection ([6c8afa9](https://github.com/anthropics/anthropic-sdk-python/commit/6c8afa9c9fb667d824d0630de22c1662e85d7bd3)) * **internal:** refactor pydantic v1 test setup ([cb5444b](https://github.com/anthropics/anthropic-sdk-python/commit/cb5444be33dd798748fc5d0709ce1d47d5d4c012)) * **internal:** run tests in an isolated environment ([9adb089](https://github.com/anthropics/anthropic-sdk-python/commit/9adb089e2fb0d67b23b389128e2375a134564ef5)) * **internal:** update pyright exclude list ([85961ef](https://github.com/anthropics/anthropic-sdk-python/commit/85961ef1d4cec5243c2e74160df65aef3090a59a)) * update github action ([1e6a135](https://github.com/anthropics/anthropic-sdk-python/commit/1e6a1353d045b26cea766fc6bcf7331159522fa5)) ## 0.64.0 (2025-08-13) Full Changelog: [v0.63.0...v0.64.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.63.0...v0.64.0) ### Features * **api:** makes 1 hour TTL Cache Control generally available ([35201ba](https://github.com/anthropics/anthropic-sdk-python/commit/35201baef190c354a803278aa926490ff6069abf)) ### Chores * deprecate older claude-3-5 sonnet models ([#1116](https://github.com/anthropics/anthropic-sdk-python/issues/1116)) ([3e8e10d](https://github.com/anthropics/anthropic-sdk-python/commit/3e8e10dc706e4fb272db78aec4c7678f842c54af)) ## 0.63.0 (2025-08-12) Full Changelog: [v0.62.0...v0.63.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.62.0...v0.63.0) ### Features * **betas:** add context-1m-2025-08-07 ([57a80e7](https://github.com/anthropics/anthropic-sdk-python/commit/57a80e7a2cf6813db6633516dbb5bb65a6e85122)) ### Chores * **internal:** detect breaking changes when removing endpoints ([5c62d7b](https://github.com/anthropics/anthropic-sdk-python/commit/5c62d7bdaf8a180dcb6bc30c17a6bdf13d976ab2)) * **internal:** update comment in script ([9e9d69c](https://github.com/anthropics/anthropic-sdk-python/commit/9e9d69cdd98a838adac734a0748e1f52ccd4faa4)) * **internal:** update test skipping reason ([b18a3d5](https://github.com/anthropics/anthropic-sdk-python/commit/b18a3d55a8f75ba3257ec83283d31bcb82548713)) * update @stainless-api/prism-cli to v5.15.0 ([55cb0a1](https://github.com/anthropics/anthropic-sdk-python/commit/55cb0a1d1f42f6bcae74c6d1946f927534e30276)) ## 0.62.0 (2025-08-08) Full Changelog: [v0.61.0...v0.62.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.61.0...v0.62.0) ### Features * **api:** search result content blocks ([1ae15cd](https://github.com/anthropics/anthropic-sdk-python/commit/1ae15cd58da2c58dfb85276860ec407401c04cc6)) ## 0.61.0 (2025-08-05) Full Changelog: [v0.60.0...v0.61.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.60.0...v0.61.0) ### Features * **api:** add claude-opus-4-1-20250805 ([baae0ee](https://github.com/anthropics/anthropic-sdk-python/commit/baae0ee315d645aaa55b01a9eae0549ea42b7cb5)) * **api:** adds support for text_editor_20250728 tool ([9ad8fe5](https://github.com/anthropics/anthropic-sdk-python/commit/9ad8fe53d6878715e5d423aedba6cc23109e47ea)) * **client:** support file upload requests ([a9bd98a](https://github.com/anthropics/anthropic-sdk-python/commit/a9bd98ab54438528cb8110cf1f4efaa2b20df959)) ### Chores * **client:** add TextEditor_20250429 tool ([ec207c5](https://github.com/anthropics/anthropic-sdk-python/commit/ec207c52d6c15ef7232a3f997aaa6d95fcbef9f3)) * **internal:** codegen related update ([4498057](https://github.com/anthropics/anthropic-sdk-python/commit/44980570bd92204177f069b5653f08dbda3ea018)) * **internal:** fix ruff target version ([3cfa202](https://github.com/anthropics/anthropic-sdk-python/commit/3cfa2023ecee6e8da5e3b71b4391477694d0475e)) ## 0.60.0 (2025-07-28) Full Changelog: [v0.59.0...v0.60.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.59.0...v0.60.0) ### Features * update streaming error message to say 'required' not 'recommended' ([57120c8](https://github.com/anthropics/anthropic-sdk-python/commit/57120c8e0579ca3d3218ce043cc46c82345a925e)) * update streaming error message to say 'required' not 'recommended' ([3b47368](https://github.com/anthropics/anthropic-sdk-python/commit/3b47368ca00a51bc4876066af502b0a21a6b6a60)) ### Bug Fixes * **vertex:** add missing beta methods ([#1004](https://github.com/anthropics/anthropic-sdk-python/issues/1004)) ([f8e9cb4](https://github.com/anthropics/anthropic-sdk-python/commit/f8e9cb40b1832eaa7f319d8b051b258a7320cbd4)) ### Chores * **project:** add settings file for vscode ([1c4a9b1](https://github.com/anthropics/anthropic-sdk-python/commit/1c4a9b17e39e76c7260b44b6b666dbf2450a6d19)) ## 0.59.0 (2025-07-23) Full Changelog: [v0.58.2...v0.59.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.58.2...v0.59.0) ### Features * **api:** removed older deprecated models ([38998fd](https://github.com/anthropics/anthropic-sdk-python/commit/38998fdab79b62349481c9c49579a825a5a33761)) ### Bug Fixes * **parsing:** ignore empty metadata ([7099f32](https://github.com/anthropics/anthropic-sdk-python/commit/7099f32a401b1f2a08b358562e325571a5fce8f6)) * **parsing:** parse extra field types ([dbea8a4](https://github.com/anthropics/anthropic-sdk-python/commit/dbea8a40469c30533e30dc1762bfba83159f090d)) ### Chores * **internal:** version bump ([5defffa](https://github.com/anthropics/anthropic-sdk-python/commit/5defffa3ab4b0299759f2a45ffa3f7a49e8c2ba5)) ## 0.58.2 (2025-07-18) Full Changelog: [v0.58.1...v0.58.2](https://github.com/anthropics/anthropic-sdk-python/compare/v0.58.1...v0.58.2) ### Chores * **internal:** version bump ([cd5d1ad](https://github.com/anthropics/anthropic-sdk-python/commit/cd5d1adc34e488f1c9e3a6d2a46f69e5c168e3f6)) ## 0.58.1 (2025-07-18) Full Changelog: [v0.58.0...v0.58.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.58.0...v0.58.1) ### Chores * **internal:** version bump ([31c3b38](https://github.com/anthropics/anthropic-sdk-python/commit/31c3b380e5ceab20789080c65cef9bd74e318a3e)) ## 0.58.0 (2025-07-18) Full Changelog: [v0.57.1...v0.58.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.57.1...v0.58.0) ### Features * clean up environment call outs ([4f64e9c](https://github.com/anthropics/anthropic-sdk-python/commit/4f64e9c1bdb142bbcff2072baa709afdea348889)) ### Bug Fixes * **client:** don't send Content-Type header on GET requests ([727268f](https://github.com/anthropics/anthropic-sdk-python/commit/727268f2bd4cb42aa1472e1f0c6a92fd6a5cb122)) * **parsing:** correctly handle nested discriminated unions ([44dd47e](https://github.com/anthropics/anthropic-sdk-python/commit/44dd47e15ed91af912eac4791421e11504c3094b)) ### Chores * **internal:** bump pinned h11 dep ([9a947e1](https://github.com/anthropics/anthropic-sdk-python/commit/9a947e1061021529937395758acb4d77b685a68b)) * **internal:** codegen related update ([33f2b34](https://github.com/anthropics/anthropic-sdk-python/commit/33f2b3468a971ca2ce6eb8b823b26d45092303f7)) * **internal:** version bump ([5f0f5ad](https://github.com/anthropics/anthropic-sdk-python/commit/5f0f5adba5392facf8331451c449513ae005a054)) * **package:** mark python 3.13 as supported ([703d557](https://github.com/anthropics/anthropic-sdk-python/commit/703d55747456a3825ba8c79cd492d6e64276dc15)) * **readme:** fix version rendering on pypi ([dd956a6](https://github.com/anthropics/anthropic-sdk-python/commit/dd956a616a45191398aac9061eaaa5f3b7cbf2f6)) ### Documentation * model in examples ([89b6925](https://github.com/anthropics/anthropic-sdk-python/commit/89b69256dcbd107970d0763afc62f2cbf08e3d5a)) * model in examples ([1eccecb](https://github.com/anthropics/anthropic-sdk-python/commit/1eccecbec9e0c0cde748695e0cb1bc61ac8ffca5)) ## 0.57.1 (2025-07-03) Full Changelog: [v0.57.0...v0.57.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.57.0...v0.57.1) ### Chores * **api:** update BetaCitationSearchResultLocation ([e0735b4](https://github.com/anthropics/anthropic-sdk-python/commit/e0735b45216fc97866492bf2fff50ea7bc9768ef)) * **internal:** version bump ([d368831](https://github.com/anthropics/anthropic-sdk-python/commit/d3688311d7b175986cff8e87ccc6e4d3159e43f4)) ## 0.57.0 (2025-07-03) Full Changelog: [v0.56.0...v0.57.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.56.0...v0.57.0) ### Features * **api:** add support for Search Result Content Blocks ([4896178](https://github.com/anthropics/anthropic-sdk-python/commit/4896178d23832e4c84775571e8919c690ff998a1)) ### Bug Fixes * improve timeout/network error message to be more helpful ([347fb57](https://github.com/anthropics/anthropic-sdk-python/commit/347fb57c49129ff1fdac19859eb4c80808ed0711)) ### Chores * **ci:** change upload type ([4dc4178](https://github.com/anthropics/anthropic-sdk-python/commit/4dc4178d0a1eaeafc248deac4e08cc782f778600)) * **internal:** version bump ([363629c](https://github.com/anthropics/anthropic-sdk-python/commit/363629cbc85d1e81d1e503d224dc8c7a3d1fa113)) * **stream:** improve get_final_text() error message ([#979](https://github.com/anthropics/anthropic-sdk-python/issues/979)) ([5ae0a33](https://github.com/anthropics/anthropic-sdk-python/commit/5ae0a3303f8369575d9ebefe5b2c45cc435facdb)) ### Documentation * fix vertex id ([f7392c7](https://github.com/anthropics/anthropic-sdk-python/commit/f7392c7789fc2d329ab63c4d2ed7ba0d1dc0c7c0)) * fix vertex id ([92fe132](https://github.com/anthropics/anthropic-sdk-python/commit/92fe1329a9a8a31de2fe71b40c4fdd84fb033dae)) * update model in readme ([1a4df78](https://github.com/anthropics/anthropic-sdk-python/commit/1a4df783a75589dce9826a5c0564692ed0d7d7fb)) * update models and non-beta ([a54e65c](https://github.com/anthropics/anthropic-sdk-python/commit/a54e65c5bc9dd1ac188ea9c166943548cc6f7c08)) * update more models ([9e3dd6a](https://github.com/anthropics/anthropic-sdk-python/commit/9e3dd6afc565a6777f96ab05a28dcf2b4b9591da)) ## 0.56.0 (2025-07-01) Full Changelog: [v0.55.0...v0.56.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.55.0...v0.56.0) ### Features * **bedrock:** automatically infer AWS Region ([#974](https://github.com/anthropics/anthropic-sdk-python/issues/974)) ([f648e09](https://github.com/anthropics/anthropic-sdk-python/commit/f648e09c43ea227a7a388cbdd21e8ddb762963e4)) * **vertex:** support global region endpoint ([1fd1adf](https://github.com/anthropics/anthropic-sdk-python/commit/1fd1adf736e4e5a3e16819c052903dfe4a436132)) ### Bug Fixes * **ci:** correct conditional ([18e625a](https://github.com/anthropics/anthropic-sdk-python/commit/18e625a1a6de15ff7729149c19b8c22191ed8622)) * **ci:** release-doctor — report correct token name ([c91f50d](https://github.com/anthropics/anthropic-sdk-python/commit/c91f50dbd7057ea465b9d71795488cdae8c1a13a)) * **tests:** avoid deprecation warnings ([71b432f](https://github.com/anthropics/anthropic-sdk-python/commit/71b432f2d22d72f6763d7042677cb43122302ded)) ### Chores * **ci:** only run for pushes and fork pull requests ([447b793](https://github.com/anthropics/anthropic-sdk-python/commit/447b793baf8ba4df63a8fcfcd870a85dd2d07f07)) * **internal:** add breaking change detection ([e6d0eca](https://github.com/anthropics/anthropic-sdk-python/commit/e6d0eca3fc5c918b56e42fbe46fcf9bedd26ca4d)) * **internal:** codegen related update ([f88517b](https://github.com/anthropics/anthropic-sdk-python/commit/f88517bfb56969674b82193b788d2043806e5a39)) * **internal:** codegen related update ([a385cb9](https://github.com/anthropics/anthropic-sdk-python/commit/a385cb9270f8214907ce1f4923e16537ac10cdab)) * **internal:** codegen related update ([9d4b537](https://github.com/anthropics/anthropic-sdk-python/commit/9d4b537be3e248f2ce0d98721f9bbbdc32b75575)) * **internal:** codegen related update ([6a3a6fe](https://github.com/anthropics/anthropic-sdk-python/commit/6a3a6fe3743ee448a83f294d394c5bf9b214176f)) * **internal:** codegen related update ([28704a6](https://github.com/anthropics/anthropic-sdk-python/commit/28704a63eb20f6ed78f13b424190cac14aca8a0f)) * **tests:** run tests with min and max supported Python versions by default ([0ad8534](https://github.com/anthropics/anthropic-sdk-python/commit/0ad85343fbe4d2934aa826fde36c7927f7b57803)) * **tests:** skip some failing tests on the latest python versions ([f63a2d2](https://github.com/anthropics/anthropic-sdk-python/commit/f63a2d29d5c56175078eaf5c67a142aec0937174)) ## 0.55.0 (2025-06-23) Full Changelog: [v0.54.0...v0.55.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.54.0...v0.55.0) ### Features * **api:** api update ([4b2134e](https://github.com/anthropics/anthropic-sdk-python/commit/4b2134e5ec3fecab7c56f483b8db87b403a08e05)) * **api:** api update ([2093bff](https://github.com/anthropics/anthropic-sdk-python/commit/2093bfff2a6c25573eaa2a4667f1e1d0e2d89e24)) * **api:** manual updates ([c80fda8](https://github.com/anthropics/anthropic-sdk-python/commit/c80fda8cbd157fbbd23895d034cc7bb7a7614569)) * **client:** add support for aiohttp ([3b03295](https://github.com/anthropics/anthropic-sdk-python/commit/3b03295f15a02ba629d1bdc77e330c2e6043b83e)) ### Bug Fixes * **client:** correctly parse binary response | stream ([d93817d](https://github.com/anthropics/anthropic-sdk-python/commit/d93817d9d761bd5e16b35f3c2973122a9c122240)) * **internal:** revert unintentional changes ([bb3beab](https://github.com/anthropics/anthropic-sdk-python/commit/bb3beab10668be177d6bb573607ef6951a238b24)) * **tests:** fix: tests which call HTTP endpoints directly with the example parameters ([ee69d74](https://github.com/anthropics/anthropic-sdk-python/commit/ee69d74cc40f749280a29afb12420c117d08ef34)) * **tests:** suppress warnings in tests when running on the latest Python versions ([#982](https://github.com/anthropics/anthropic-sdk-python/issues/982)) ([740da21](https://github.com/anthropics/anthropic-sdk-python/commit/740da21b563c6ffe7618edf1dcd658bb894b2edf)) ### Chores * **ci:** enable for pull requests ([08f2dd2](https://github.com/anthropics/anthropic-sdk-python/commit/08f2dd2bd28958c08a3c82fcf00a0fc7d4e2807c)) * **internal:** update conftest.py ([1174a62](https://github.com/anthropics/anthropic-sdk-python/commit/1174a6214624ff8cd64edb121d4ff09e9af6b717)) * **internal:** version bump ([7241eaa](https://github.com/anthropics/anthropic-sdk-python/commit/7241eaa25b6f40bb55f61e766a996a3a18a53a02)) * **readme:** update badges ([00661c2](https://github.com/anthropics/anthropic-sdk-python/commit/00661c275e120314f76bbd480c0267383e992638)) * **tests:** add tests for httpx client instantiation & proxies ([b831d88](https://github.com/anthropics/anthropic-sdk-python/commit/b831d8833010c629143041b4b385929ca9c2198d)) * **tests:** run tests in parallel ([4b24a79](https://github.com/anthropics/anthropic-sdk-python/commit/4b24a791b76c2176de1f35118901da533a10b991)) ### Documentation * **client:** fix httpx.Timeout documentation reference ([b0138b1](https://github.com/anthropics/anthropic-sdk-python/commit/b0138b1b2af3c73e568659c7e717fc955eb976b0)) ## 0.54.0 (2025-06-10) Full Changelog: [v0.53.0...v0.54.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.53.0...v0.54.0) ### Features * **client:** add support for fine-grained-tool-streaming-2025-05-14 ([07ec081](https://github.com/anthropics/anthropic-sdk-python/commit/07ec08119dbc328934fea5ec6eacd00c8dbda089)) ### Bug Fixes * **httpx:** resolve conflict between default transport and proxy settings ([#969](https://github.com/anthropics/anthropic-sdk-python/issues/969)) ([a6efded](https://github.com/anthropics/anthropic-sdk-python/commit/a6efdedcfef881ae3466bb77d92d0338c8338e20)) * **tests:** update test ([99c2433](https://github.com/anthropics/anthropic-sdk-python/commit/99c243363e94f5f3f627cb8b80e3f238503c89f5)) ### Chores * **internal:** version bump ([45029f4](https://github.com/anthropics/anthropic-sdk-python/commit/45029f41c96f62f26ead99a5989c9ad974fc21b9)) ### Documentation * **contributing:** fix uv script for bootstrapping ([d2bde52](https://github.com/anthropics/anthropic-sdk-python/commit/d2bde52286ee8fa65995e73c579a8962087c1da4)) ## 0.53.0 (2025-06-09) Full Changelog: [v0.52.2...v0.53.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.52.2...v0.53.0) ### Features * **client:** add follow_redirects request option ([e5238c0](https://github.com/anthropics/anthropic-sdk-python/commit/e5238c0d77aaab054b58e0ec046fe7a981eecadf)) * **client:** add support for new text_editor_20250429 tool ([b3b3f5b](https://github.com/anthropics/anthropic-sdk-python/commit/b3b3f5b27b9eb3d6f2d4d242fd473aec84fb99a4)) ### Bug Fixes * **client:** deprecate BetaBase64PDFBlock in favor of BetaRequestDocumentBlock ([5ac58e9](https://github.com/anthropics/anthropic-sdk-python/commit/5ac58e97d7b8502db477cf15169ac18c2c0916c9)) * **internal:** fix typing remapping ([6c415da](https://github.com/anthropics/anthropic-sdk-python/commit/6c415da0b2713505b0deaa586f92b2a549b5d3ca)) ### Chores * **internal:** codegen related update ([94812ec](https://github.com/anthropics/anthropic-sdk-python/commit/94812ec4c75c93268c5dec21d2659dd3b0725c32)) * **internal:** version bump ([41ce701](https://github.com/anthropics/anthropic-sdk-python/commit/41ce701f67858e5bfb0f68b8f30f114d9c8e5712)) * **tests:** improve testing by extracting fixtures ([68c62cc](https://github.com/anthropics/anthropic-sdk-python/commit/68c62cc7b97e27985eff22d65b9ba1854eea7a53)) ## 0.52.2 (2025-06-02) Full Changelog: [v0.52.1...v0.52.2](https://github.com/anthropics/anthropic-sdk-python/compare/v0.52.1...v0.52.2) ### Bug Fixes * **client:** fix issue with server_tool_use input tracking and improve tests ([3fe3fa2](https://github.com/anthropics/anthropic-sdk-python/commit/3fe3fa278bbd44fd01c889807154c37259bf617d)) * **docs:** remove reference to rye shell ([2b3d677](https://github.com/anthropics/anthropic-sdk-python/commit/2b3d677a8df49e1c9a26a594436f1ace8af7d3af)) ### Chores * **docs:** remove unnecessary param examples ([6b129f4](https://github.com/anthropics/anthropic-sdk-python/commit/6b129f4fd00ca296b53368966349bcce0ee0e3a0)) ### Refactors * **pkg:** switch from rye to uv ([f553908](https://github.com/anthropics/anthropic-sdk-python/commit/f55390804154b73d9a639fb25b93f21106bcf569)) ## 0.52.1 (2025-05-28) Full Changelog: [v0.52.0...v0.52.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.52.0...v0.52.1) ### Bug Fixes * **example:** logo.png was broken ([#1021](https://github.com/anthropics/anthropic-sdk-python/issues/1021)) ([1ee8314](https://github.com/anthropics/anthropic-sdk-python/commit/1ee83149809e0193612b62f9b443a0acac5d3d37)) ### Chores * **examples:** show how to pass an authorization token to an MCP server ([18be7f3](https://github.com/anthropics/anthropic-sdk-python/commit/18be7f3194ed1171c3ab937b37c2eb9dbf93a6c5)) * **internal:** fix release workflows ([be9af1f](https://github.com/anthropics/anthropic-sdk-python/commit/be9af1f6fd5c0b4befa61fc7ec01ec52cec9ecd5)) ## 0.52.0 (2025-05-22) Full Changelog: [v0.51.0...v0.52.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.51.0...v0.52.0) ### Features * **api:** add claude 4 models, files API, code execution tool, MCP connector and more ([9c48bc6](https://github.com/anthropics/anthropic-sdk-python/commit/9c48bc6211e2b36cab4c25b67a3dfa4e679aa046)) ### Bug Fixes * **package:** support direct resource imports ([6d73bab](https://github.com/anthropics/anthropic-sdk-python/commit/6d73bab63cc666f0de65ec67f7e2b55a3de1b8cf)) ### Chores * **ci:** fix installation instructions ([ca374e5](https://github.com/anthropics/anthropic-sdk-python/commit/ca374e587c283d46afedfa0e571bc4126f252644)) * **ci:** upload sdks to package manager ([fde0c44](https://github.com/anthropics/anthropic-sdk-python/commit/fde0c44a2e4cc3afe34b644f47e3cca986d210c6)) * **internal:** avoid errors for isinstance checks on proxies ([ef4be3f](https://github.com/anthropics/anthropic-sdk-python/commit/ef4be3f6ae02632d1d67ef6d4ac9d3bacef5e934)) * **internal:** codegen related update ([40359d9](https://github.com/anthropics/anthropic-sdk-python/commit/40359d9db8c5c5868a74de85b84c5e9ccbed5ae4)) ### Documentation * add security warning for overriding parameters ([#1008](https://github.com/anthropics/anthropic-sdk-python/issues/1008)) ([9f52239](https://github.com/anthropics/anthropic-sdk-python/commit/9f52239dda32f26c1fdd999723a124d1bc87dc18)) ## 0.51.0 (2025-05-07) Full Changelog: [v0.50.0...v0.51.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.50.0...v0.51.0) ### Features * **api:** adds web search capabilities to the Claude API ([bec0cf9](https://github.com/anthropics/anthropic-sdk-python/commit/bec0cf93c2d7cb47c921236a14c8569e0e22793e)) ### Bug Fixes * **pydantic v1:** more robust ModelField.annotation check ([c50f406](https://github.com/anthropics/anthropic-sdk-python/commit/c50f406767d8e7737a2754d6e1488d8a19216ac0)) * **sockets:** handle non-portable socket flags ([#935](https://github.com/anthropics/anthropic-sdk-python/issues/935)) ([205c8dd](https://github.com/anthropics/anthropic-sdk-python/commit/205c8dda371caa3b393d3cfb4d323714b1fab336)) ### Chores * broadly detect json family of content-type headers ([66bbb3a](https://github.com/anthropics/anthropic-sdk-python/commit/66bbb3a6689a4e2dd4915a7a2940dec53e2b8eb9)) * **ci:** only use depot for staging repos ([c867a11](https://github.com/anthropics/anthropic-sdk-python/commit/c867a11af37416c0d513aa177f77e1bcd0d70949)) * **ci:** run on more branches and use depot runners ([95f5f17](https://github.com/anthropics/anthropic-sdk-python/commit/95f5f17be0ab05ed4e258ccc488d8cf55ffb8f29)) * **internal:** add back missing custom modifications for Web Search ([f43ba69](https://github.com/anthropics/anthropic-sdk-python/commit/f43ba69d5337e5d99f7ca9bd2e773cc57bae5d1c)) * **internal:** minor formatting changes ([8afef08](https://github.com/anthropics/anthropic-sdk-python/commit/8afef086af194df1e4b0d6d25b7fbe4d74bd6850)) * use lazy imports for resources ([704be81](https://github.com/anthropics/anthropic-sdk-python/commit/704be817f436d92d96915cf02608b6827e06945f)) ## 0.50.0 (2025-04-22) Full Changelog: [v0.49.0...v0.50.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.49.0...v0.50.0) ### Features * **api:** extract ContentBlockDelta events into their own schemas ([#920](https://github.com/anthropics/anthropic-sdk-python/issues/920)) ([ae773d6](https://github.com/anthropics/anthropic-sdk-python/commit/ae773d673a7d3cbb28eebce0df9c526f1e855435)) * **api:** manual updates ([46ac1f8](https://github.com/anthropics/anthropic-sdk-python/commit/46ac1f8d1cfa21fbe9df4545d748211c0f3c10e0)) * **api:** manual updates ([48d9739](https://github.com/anthropics/anthropic-sdk-python/commit/48d9739ad741c72d6ecab5200ca53151a604416f)) * **api:** manual updates ([66e8cc3](https://github.com/anthropics/anthropic-sdk-python/commit/66e8cc3fb207a889b1df1028db73291b9800f8f9)) * **api:** manual updates ([a74746e](https://github.com/anthropics/anthropic-sdk-python/commit/a74746e0df0b3c9b85a42e95a80c1db763379e1b)) ### Bug Fixes * **ci:** ensure pip is always available ([#907](https://github.com/anthropics/anthropic-sdk-python/issues/907)) ([3632687](https://github.com/anthropics/anthropic-sdk-python/commit/36326871c1304fbb1dad56e3e5bc71659bbf0df1)) * **ci:** remove publishing patch ([#908](https://github.com/anthropics/anthropic-sdk-python/issues/908)) ([cae0323](https://github.com/anthropics/anthropic-sdk-python/commit/cae032381bd73e86174b5fde2efaf046e96f5e6a)) * **client:** deduplicate stop reason type ([#913](https://github.com/anthropics/anthropic-sdk-python/issues/913)) ([3ab0194](https://github.com/anthropics/anthropic-sdk-python/commit/3ab0194550aa9893cc948c3d658a965817d64ccd)) * **client:** send all configured auth headers ([#929](https://github.com/anthropics/anthropic-sdk-python/issues/929)) ([9d2581e](https://github.com/anthropics/anthropic-sdk-python/commit/9d2581e79f31effb34958e633b59b19aa3681875)) * **perf:** optimize some hot paths ([cff76cb](https://github.com/anthropics/anthropic-sdk-python/commit/cff76cb00ba0b7839141d84d1f751516a11240c3)) * **perf:** skip traversing types for NotGiven values ([dadac7f](https://github.com/anthropics/anthropic-sdk-python/commit/dadac7fa7207bef547db39b9003a46512a945a78)) * **project:** bump httpx minimum version to 0.25.0 ([b554138](https://github.com/anthropics/anthropic-sdk-python/commit/b554138c2f5d73dd915092972411f7ab787cfe42)), closes [#902](https://github.com/anthropics/anthropic-sdk-python/issues/902) * **types:** handle more discriminated union shapes ([#906](https://github.com/anthropics/anthropic-sdk-python/issues/906)) ([2fc179a](https://github.com/anthropics/anthropic-sdk-python/commit/2fc179a4d29b720263e84c90f37d078ffab860ad)) * **vertex:** explicitly include requests extra ([2b1221b](https://github.com/anthropics/anthropic-sdk-python/commit/2b1221b76bfcc0dfaa14d94e7f6a3ddc303f3715)) ### Chores * add hash of OpenAPI spec/config inputs to .stats.yml ([#912](https://github.com/anthropics/anthropic-sdk-python/issues/912)) ([ddf7835](https://github.com/anthropics/anthropic-sdk-python/commit/ddf78352c9e589f6102f9373cc01bee9333d15d8)) * **ci:** add timeout thresholds for CI jobs ([7226a5c](https://github.com/anthropics/anthropic-sdk-python/commit/7226a5ccef181041fcea0fffcbc0ed395a700df3)) * **client:** minor internal fixes ([99b9a38](https://github.com/anthropics/anthropic-sdk-python/commit/99b9a387c60347471763c9970f7767ecd4cc04d1)) * **internal:** add back release workflow ([ce18972](https://github.com/anthropics/anthropic-sdk-python/commit/ce189722eefedc794111899d54048bddaa82d17d)) * **internal:** base client updates ([2e08c71](https://github.com/anthropics/anthropic-sdk-python/commit/2e08c714cebd4a7df87f80902220b1502d371d04)) * **internal:** bump pyright version ([d9ea30e](https://github.com/anthropics/anthropic-sdk-python/commit/d9ea30ead2cf76676a3b3ac0181a1616afe8323b)) * **internal:** bump rye to 0.44.0 ([#905](https://github.com/anthropics/anthropic-sdk-python/issues/905)) ([e1a1b14](https://github.com/anthropics/anthropic-sdk-python/commit/e1a1b142c8f385d288cf5395917e6380add556ea)) * **internal:** expand CI branch coverage ([#934](https://github.com/anthropics/anthropic-sdk-python/issues/934)) ([b23fdc9](https://github.com/anthropics/anthropic-sdk-python/commit/b23fdc940d5a8eff35746b39a59678e8c633c289)) * **internal:** fix list file params ([cfbaaf9](https://github.com/anthropics/anthropic-sdk-python/commit/cfbaaf9650a963cf15febb4d141f40f18b37c2ef)) * **internal:** import ordering changes ([#895](https://github.com/anthropics/anthropic-sdk-python/issues/895)) ([b8da2f7](https://github.com/anthropics/anthropic-sdk-python/commit/b8da2f748f478fc83c5f13f1d0dcd2eaf85922e0)) * **internal:** import reformatting ([5e6cd74](https://github.com/anthropics/anthropic-sdk-python/commit/5e6cd74bc383b812698b2d572ff9b016aae94b5e)) * **internal:** reduce CI branch coverage ([07e813f](https://github.com/anthropics/anthropic-sdk-python/commit/07e813f9c1873c5addc1d2adacdb387e8db5f3da)) * **internal:** refactor retries to not use recursion ([4354e82](https://github.com/anthropics/anthropic-sdk-python/commit/4354e82dc891cfa973d056aabada54f461979d2c)) * **internal:** remove CI condition ([#916](https://github.com/anthropics/anthropic-sdk-python/issues/916)) ([043b56b](https://github.com/anthropics/anthropic-sdk-python/commit/043b56b9772d49965cdeec649f2b6c7bdf0249a5)) * **internal:** remove extra empty newlines ([#904](https://github.com/anthropics/anthropic-sdk-python/issues/904)) ([cfe8f6e](https://github.com/anthropics/anthropic-sdk-python/commit/cfe8f6e4e1a5be51a2bd7ced23258b4159564ae7)) * **internal:** remove trailing character ([#924](https://github.com/anthropics/anthropic-sdk-python/issues/924)) ([dc8e781](https://github.com/anthropics/anthropic-sdk-python/commit/dc8e7816a9e8cae3f4f8ccf4b9723f9093b3f05e)) * **internal:** remove unused http client options forwarding ([#890](https://github.com/anthropics/anthropic-sdk-python/issues/890)) ([e0428bf](https://github.com/anthropics/anthropic-sdk-python/commit/e0428bfdffb6adb2e21c6ca365a26816eb699006)) * **internal:** slight transform perf improvement ([#931](https://github.com/anthropics/anthropic-sdk-python/issues/931)) ([3ed4e5e](https://github.com/anthropics/anthropic-sdk-python/commit/3ed4e5eebb7bc5d42c6888cda0388c84ce74fa12)) * **internal:** update config ([#914](https://github.com/anthropics/anthropic-sdk-python/issues/914)) ([a697234](https://github.com/anthropics/anthropic-sdk-python/commit/a697234637b84b2a488309bdb851db2efe17190c)) * **internal:** update models test ([b1e031d](https://github.com/anthropics/anthropic-sdk-python/commit/b1e031dfee560629007dab1120872ca489e1e6c2)) * **internal:** update pyright settings ([38bdc6c](https://github.com/anthropics/anthropic-sdk-python/commit/38bdc6cd3436734f29a39b3e4b2c011733a4c0d3)) * **internal:** variable name and test updates ([#925](https://github.com/anthropics/anthropic-sdk-python/issues/925)) ([f5d0809](https://github.com/anthropics/anthropic-sdk-python/commit/f5d08095216744373f1b5d25f65873ca674796e8)) * **tests:** improve enum examples ([#932](https://github.com/anthropics/anthropic-sdk-python/issues/932)) ([808aaf3](https://github.com/anthropics/anthropic-sdk-python/commit/808aaf32d77ab82e79bb8e37c0cb371403726be1)) * **vertex:** improve error message when missing extra ([15dc4cb](https://github.com/anthropics/anthropic-sdk-python/commit/15dc4cb5297304420fd1d7be8e87697bf60d8b2d)) ### Documentation * revise readme docs about nested params ([#900](https://github.com/anthropics/anthropic-sdk-python/issues/900)) ([0f80ab0](https://github.com/anthropics/anthropic-sdk-python/commit/0f80ab0ffcd5c2d854f01364b1cfe2517e04b40b)) * swap examples used in readme ([#928](https://github.com/anthropics/anthropic-sdk-python/issues/928)) ([96ff1c7](https://github.com/anthropics/anthropic-sdk-python/commit/96ff1c7b6a09907c33def5afd9f61bb597e897b1)) ## 0.49.0 (2025-02-28) Full Changelog: [v0.48.0...v0.49.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.48.0...v0.49.0) ### Features * **api:** add support for disabling tool calls ([#888](https://github.com/anthropics/anthropic-sdk-python/issues/888)) ([bfde3d2](https://github.com/anthropics/anthropic-sdk-python/commit/bfde3d2978f78ee43db351a78fe1b078eb073394)) ### Chores * **docs:** update client docstring ([#887](https://github.com/anthropics/anthropic-sdk-python/issues/887)) ([4d3ec5e](https://github.com/anthropics/anthropic-sdk-python/commit/4d3ec5ec5b9c9aabb7cc7f27345809c347c56d89)) ### Documentation * update URLs from stainlessapi.com to stainless.com ([#885](https://github.com/anthropics/anthropic-sdk-python/issues/885)) ([312364b](https://github.com/anthropics/anthropic-sdk-python/commit/312364b9b5025ba16f0081e6e53b478c3a4fb089)) ## 0.48.0 (2025-02-27) Full Changelog: [v0.47.2...v0.48.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.47.2...v0.48.0) ### Features * **api:** add URL source blocks for images and PDFs ([#884](https://github.com/anthropics/anthropic-sdk-python/issues/884)) ([e6b3a70](https://github.com/anthropics/anthropic-sdk-python/commit/e6b3a70ffbd830a8bdd87c0938897eebc6d5ee33)) ### Documentation * add thinking examples ([f463248](https://github.com/anthropics/anthropic-sdk-python/commit/f46324863dabe4efc4adec7361be5d6888fe4dd5)) ## 0.47.2 (2025-02-25) Full Changelog: [v0.47.1...v0.47.2](https://github.com/anthropics/anthropic-sdk-python/compare/v0.47.1...v0.47.2) ### Bug Fixes * **beta:** add thinking to beta.messages.stream ([69e3db1](https://github.com/anthropics/anthropic-sdk-python/commit/69e3db1de0c584c06ac450cc181144abc1602f13)) ### Chores * **internal:** properly set __pydantic_private__ ([#879](https://github.com/anthropics/anthropic-sdk-python/issues/879)) ([3537a3b](https://github.com/anthropics/anthropic-sdk-python/commit/3537a3bb229412bcd89e79bfda45e89727187244)) ## 0.47.1 (2025-02-24) Full Changelog: [v0.47.0...v0.47.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.47.0...v0.47.1) ### Chores * **internal:** update spec ([#871](https://github.com/anthropics/anthropic-sdk-python/issues/871)) ([916be18](https://github.com/anthropics/anthropic-sdk-python/commit/916be1806d3a86ec5abaef7c00227cd918b92274)) * update large max_tokens error message ([40c71df](https://github.com/anthropics/anthropic-sdk-python/commit/40c71df4b6e7760648f42a5f0995bcc93dafbd17)) ## 0.47.0 (2025-02-24) Full Changelog: [v0.46.0...v0.47.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.46.0...v0.47.0) ### Features * **api:** add claude-3.7 + support for thinking ([c5387e6](https://github.com/anthropics/anthropic-sdk-python/commit/c5387e69e799f14e44006ea4e54fdf32f2f74393)) * **client:** add more status exceptions ([#854](https://github.com/anthropics/anthropic-sdk-python/issues/854)) ([00d9512](https://github.com/anthropics/anthropic-sdk-python/commit/00d95126aff50158c7849d651c35a88ea81ff969)) * **client:** allow passing `NotGiven` for body ([#868](https://github.com/anthropics/anthropic-sdk-python/issues/868)) ([8ab445e](https://github.com/anthropics/anthropic-sdk-python/commit/8ab445e6a837854421f358f4977ff80c5e0635c8)) ### Bug Fixes * **client:** mark some request bodies as optional ([8ab445e](https://github.com/anthropics/anthropic-sdk-python/commit/8ab445e6a837854421f358f4977ff80c5e0635c8)) ### Chores * **internal:** fix devcontainers setup ([#870](https://github.com/anthropics/anthropic-sdk-python/issues/870)) ([1a21c6a](https://github.com/anthropics/anthropic-sdk-python/commit/1a21c6a3fb6969a07e9b8483ed116fb3af7bd3b2)) ## 0.46.0 (2025-02-18) Full Changelog: [v0.45.2...v0.46.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.45.2...v0.46.0) ### Features * **client:** send `X-Stainless-Read-Timeout` header ([#858](https://github.com/anthropics/anthropic-sdk-python/issues/858)) ([0e75983](https://github.com/anthropics/anthropic-sdk-python/commit/0e759835ddfa7d72d0926cab0101601e7c1e8f22)) * **jsonl:** add .close() method ([#862](https://github.com/anthropics/anthropic-sdk-python/issues/862)) ([137335c](https://github.com/anthropics/anthropic-sdk-python/commit/137335c649f3dc886875bb60bddeb0c8d8abb67b)) * **pagination:** avoid fetching when has_more: false ([#860](https://github.com/anthropics/anthropic-sdk-python/issues/860)) ([0cdb81d](https://github.com/anthropics/anthropic-sdk-python/commit/0cdb81d106c48c851fff5c9532c675b414f474b4)) ### Bug Fixes * asyncify on non-asyncio runtimes ([#864](https://github.com/anthropics/anthropic-sdk-python/issues/864)) ([f92b36d](https://github.com/anthropics/anthropic-sdk-python/commit/f92b36d4a87c6b5455945ce38e3ab3db24e9a529)) * **internal:** add back custom header naming support ([#861](https://github.com/anthropics/anthropic-sdk-python/issues/861)) ([cf851ae](https://github.com/anthropics/anthropic-sdk-python/commit/cf851ae9ee57635250beec8bedb0134aa2d79a42)) * **jsonl:** lower chunk size ([#863](https://github.com/anthropics/anthropic-sdk-python/issues/863)) ([38fb720](https://github.com/anthropics/anthropic-sdk-python/commit/38fb72043b436afc02839ad4e2a966a5ef0b0bc1)) ### Chores * **api:** update openapi spec url ([#852](https://github.com/anthropics/anthropic-sdk-python/issues/852)) ([461d821](https://github.com/anthropics/anthropic-sdk-python/commit/461d821965c61d98bf35a8b6fab5da55a2ddddef)) * **internal:** bummp ruff dependency ([#856](https://github.com/anthropics/anthropic-sdk-python/issues/856)) ([590c3fa](https://github.com/anthropics/anthropic-sdk-python/commit/590c3fa154e38f85c3cc6fcc518a6c68ee2bd234)) * **internal:** change default timeout to an int ([#855](https://github.com/anthropics/anthropic-sdk-python/issues/855)) ([3152e1a](https://github.com/anthropics/anthropic-sdk-python/commit/3152e1a135a07430404f3209fbbcb924d9d2b9a2)) * **internal:** fix tests ([fc41ba2](https://github.com/anthropics/anthropic-sdk-python/commit/fc41ba21b492e864685384e82b3eeca1ca56f4eb)) * **internal:** fix type traversing dictionary params ([#859](https://github.com/anthropics/anthropic-sdk-python/issues/859)) ([c5b700d](https://github.com/anthropics/anthropic-sdk-python/commit/c5b700d9abea1fcebc43fe12ac514512fedff0db)) * **internal:** reorder model constants ([#847](https://github.com/anthropics/anthropic-sdk-python/issues/847)) ([aadd531](https://github.com/anthropics/anthropic-sdk-python/commit/aadd5315868ce2eec17cc7d0e8ec4f0b6a4c3c87)) * **internal:** update models used in tests ([aadd531](https://github.com/anthropics/anthropic-sdk-python/commit/aadd5315868ce2eec17cc7d0e8ec4f0b6a4c3c87)) ## 0.45.2 (2025-01-27) Full Changelog: [v0.45.1...v0.45.2](https://github.com/anthropics/anthropic-sdk-python/compare/v0.45.1...v0.45.2) ### Bug Fixes * **streaming:** avoid invalid deser type error ([#845](https://github.com/anthropics/anthropic-sdk-python/issues/845)) ([72a2585](https://github.com/anthropics/anthropic-sdk-python/commit/72a2585680a4cc5d007bf93935424dbc4cecf2bd)) ## 0.45.1 (2025-01-27) Full Changelog: [v0.45.0...v0.45.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.45.0...v0.45.1) ### Bug Fixes * **streaming:** accumulate citations ([#844](https://github.com/anthropics/anthropic-sdk-python/issues/844)) ([e665f2f](https://github.com/anthropics/anthropic-sdk-python/commit/e665f2fefd4573fc45cd4c546a9480f15d18d1cd)) ### Chores * **docs:** updates ([#841](https://github.com/anthropics/anthropic-sdk-python/issues/841)) ([fb10a7d](https://github.com/anthropics/anthropic-sdk-python/commit/fb10a7d658044062e5023cd8495c80d3344af8df)) ## 0.45.0 (2025-01-23) Full Changelog: [v0.44.0...v0.45.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.44.0...v0.45.0) ### Features * **api:** add citations ([#839](https://github.com/anthropics/anthropic-sdk-python/issues/839)) ([2ec74b6](https://github.com/anthropics/anthropic-sdk-python/commit/2ec74b6ff106c6e3d7d55e3c189e345098f8575e)) * **client:** support results endpoint ([#835](https://github.com/anthropics/anthropic-sdk-python/issues/835)) ([5dd88bf](https://github.com/anthropics/anthropic-sdk-python/commit/5dd88bf2d20b8909736cc0bc1e81296ba6e322a9)) ### Chores * **internal:** minor formatting changes ([#838](https://github.com/anthropics/anthropic-sdk-python/issues/838)) ([31eb826](https://github.com/anthropics/anthropic-sdk-python/commit/31eb826deb96a17d3c0bb953c728f39412c12ada)) ## 0.44.0 (2025-01-21) Full Changelog: [v0.43.1...v0.44.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.43.1...v0.44.0) ### Features * **streaming:** add request_id getter ([#831](https://github.com/anthropics/anthropic-sdk-python/issues/831)) ([fb397e0](https://github.com/anthropics/anthropic-sdk-python/commit/fb397e0851bd874a10a69a9531483fd196fc8a55)) ### Bug Fixes * **tests:** make test_get_platform less flaky ([#830](https://github.com/anthropics/anthropic-sdk-python/issues/830)) ([f2c10ca](https://github.com/anthropics/anthropic-sdk-python/commit/f2c10cae0cbff6881bba2a41c93efdcc17e8d2ab)) ### Chores * deprecate more models ([c647e25](https://github.com/anthropics/anthropic-sdk-python/commit/c647e25c3735e4276195ee8eb0011ace3e3e0d2f)) * **internal:** avoid pytest-asyncio deprecation warning ([#832](https://github.com/anthropics/anthropic-sdk-python/issues/832)) ([2b3ceff](https://github.com/anthropics/anthropic-sdk-python/commit/2b3ceff7ef9c953e28044442821069d7de3b0154)) * **internal:** minor style changes ([#833](https://github.com/anthropics/anthropic-sdk-python/issues/833)) ([65cfb7b](https://github.com/anthropics/anthropic-sdk-python/commit/65cfb7b13324e52e2d2987c4de3ed9e5c122a40b)) ### Documentation * **raw responses:** fix duplicate `the` ([#828](https://github.com/anthropics/anthropic-sdk-python/issues/828)) ([ff850f8](https://github.com/anthropics/anthropic-sdk-python/commit/ff850f8081a72090eaaa31e09e560acd3ce18b09)) ## 0.43.1 (2025-01-17) Full Changelog: [v0.43.0...v0.43.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.43.0...v0.43.1) ### Bug Fixes * **docs:** correct results return type ([69ad511](https://github.com/anthropics/anthropic-sdk-python/commit/69ad5112596f6e9aaf5cd2d495cb57516f2afbd4)) ### Chores * **internal:** bump pyright dependency ([#822](https://github.com/anthropics/anthropic-sdk-python/issues/822)) ([f8ddb90](https://github.com/anthropics/anthropic-sdk-python/commit/f8ddb90112a432a750fd4123c747ca581cff54ab)) * **internal:** fix lint ([483cc27](https://github.com/anthropics/anthropic-sdk-python/commit/483cc277b66cb5b1a767e9d91347f22bcf69dc28)) * **streaming:** add runtime type check for better error messages ([#826](https://github.com/anthropics/anthropic-sdk-python/issues/826)) ([cf69e09](https://github.com/anthropics/anthropic-sdk-python/commit/cf69e091d230aa0befb6ace74e64357b1cf2e4cd)) * **types:** add more discriminator metadata ([#825](https://github.com/anthropics/anthropic-sdk-python/issues/825)) ([d0de8e5](https://github.com/anthropics/anthropic-sdk-python/commit/d0de8e564038cc6f801dc663b1938ac571ab47be)) ## 0.43.0 (2025-01-14) Full Changelog: [v0.42.0...v0.43.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.42.0...v0.43.0) ### Features * **api:** add message batch delete endpoint ([#802](https://github.com/anthropics/anthropic-sdk-python/issues/802)) ([9cf1e99](https://github.com/anthropics/anthropic-sdk-python/commit/9cf1e9920d3e0ce8496859b119f19eed8cd75e2b)) * **beta:** add streaming helpers for beta messages ([#819](https://github.com/anthropics/anthropic-sdk-python/issues/819)) ([d913ba3](https://github.com/anthropics/anthropic-sdk-python/commit/d913ba35eb3a95e80154b9d35e4c0a9f4a8dfeb1)) ### Bug Fixes * **client:** only call .close() when needed ([#811](https://github.com/anthropics/anthropic-sdk-python/issues/811)) ([21e0eb3](https://github.com/anthropics/anthropic-sdk-python/commit/21e0eb3c2bb043814541c9dcb68498066be1fc78)) * correctly handle deserialising `cls` fields ([#817](https://github.com/anthropics/anthropic-sdk-python/issues/817)) ([60e56a5](https://github.com/anthropics/anthropic-sdk-python/commit/60e56a5fb4413a97586df84c86765947b7ff92e5)) * **types:** allow extra properties in input schemas ([d0961c2](https://github.com/anthropics/anthropic-sdk-python/commit/d0961c2fcbe7370f145512facad8aab175798158)) ### Chores * add missing isclass check ([#806](https://github.com/anthropics/anthropic-sdk-python/issues/806)) ([1fc034d](https://github.com/anthropics/anthropic-sdk-python/commit/1fc034d784e9a8b30866a8058a4bd50ec2605fd3)) * bump testing data uri ([#800](https://github.com/anthropics/anthropic-sdk-python/issues/800)) ([641ae8d](https://github.com/anthropics/anthropic-sdk-python/commit/641ae8d412e365e44c8965222f487e30a63d57ee)) * **internal:** bump httpx dependency ([#809](https://github.com/anthropics/anthropic-sdk-python/issues/809)) ([7d678f1](https://github.com/anthropics/anthropic-sdk-python/commit/7d678f19b86ac1ad5d18ed47739d3df64187c843)) * **internal:** minor reformatting ([5a80668](https://github.com/anthropics/anthropic-sdk-python/commit/5a806684c4aaaf7d493bf9ef193e8031f7290b78)) * **internal:** update deps ([#820](https://github.com/anthropics/anthropic-sdk-python/issues/820)) ([32c3e1a](https://github.com/anthropics/anthropic-sdk-python/commit/32c3e1a63da74919dc0cb10a16b731afc2589cf5)) * **internal:** update examples ([#810](https://github.com/anthropics/anthropic-sdk-python/issues/810)) ([bb588ca](https://github.com/anthropics/anthropic-sdk-python/commit/bb588ca71a374b38797390c62bee66a0329c28a0)) * **vertex:** remove deprecated HTTP client options ([3f4eada](https://github.com/anthropics/anthropic-sdk-python/commit/3f4eada664421accec6f33a7ead44101323d0b14)) * **vertex:** remove deprecated HTTP client options ([c82f3e8](https://github.com/anthropics/anthropic-sdk-python/commit/c82f3e8ce16e8840cce94e023a82c596432f5c98)) ### Documentation * fix typos ([#812](https://github.com/anthropics/anthropic-sdk-python/issues/812)) ([8f46cae](https://github.com/anthropics/anthropic-sdk-python/commit/8f46cae8af228c9520bf95383afd1dce49198517)) * fix typos ([#813](https://github.com/anthropics/anthropic-sdk-python/issues/813)) ([ac44348](https://github.com/anthropics/anthropic-sdk-python/commit/ac443484c21e89bf0c491179613ae61709f87cb9)) * **readme:** fix misplaced period ([#816](https://github.com/anthropics/anthropic-sdk-python/issues/816)) ([4358226](https://github.com/anthropics/anthropic-sdk-python/commit/4358226929ffc3a7f1cb49489b7288ab870e636b)) ### Refactors * **stream:** make `MessageStream` wrap `Stream` directly ([#805](https://github.com/anthropics/anthropic-sdk-python/issues/805)) ([5669399](https://github.com/anthropics/anthropic-sdk-python/commit/56693993cce931fb0ae12d8890099069c4c95dff)) * **vertex:** remove deprecated HTTP client options ([#808](https://github.com/anthropics/anthropic-sdk-python/issues/808)) ([3f4eada](https://github.com/anthropics/anthropic-sdk-python/commit/3f4eada664421accec6f33a7ead44101323d0b14)) ## 0.42.0 (2024-12-17) Full Changelog: [v0.41.0...v0.42.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.41.0...v0.42.0) ### Features * **api:** general availability updates ([#795](https://github.com/anthropics/anthropic-sdk-python/issues/795)) ([0954c48](https://github.com/anthropics/anthropic-sdk-python/commit/0954c488e64a8d80d2dfa160b0ffdd8366996d2e)) ### Bug Fixes * **vertex:** remove `anthropic_version` deletion for token counting ([f613929](https://github.com/anthropics/anthropic-sdk-python/commit/f613929150591e8927af590554e71f197fc243fc)) ### Chores * **internal:** fix some typos ([#799](https://github.com/anthropics/anthropic-sdk-python/issues/799)) ([45addb6](https://github.com/anthropics/anthropic-sdk-python/commit/45addb671fde3c7e06735fbd09fee278e0ddff18)) ## 0.41.0 (2024-12-17) Full Changelog: [v0.40.0...v0.41.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.40.0...v0.41.0) ### Features * **api:** general availability updates ([5db8538](https://github.com/anthropics/anthropic-sdk-python/commit/5db8538cca2ab957ccb5460bf3f33636de0a5106)) * **api:** general availability updates ([#795](https://github.com/anthropics/anthropic-sdk-python/issues/795)) ([c8d5e43](https://github.com/anthropics/anthropic-sdk-python/commit/c8d5e43d00e0e68a68b9ecac15414135750495ff)) * **vertex:** support token counting ([6c3eded](https://github.com/anthropics/anthropic-sdk-python/commit/6c3ededeb68f878a94998d61d0c78209ec640d0c)) ### Bug Fixes * **internal:** correct support for TypeAliasType ([2f6ba9e](https://github.com/anthropics/anthropic-sdk-python/commit/2f6ba9e9f827b39b373a4b2904df04fef940001a)) ### Chores * **api:** update spec version ([#792](https://github.com/anthropics/anthropic-sdk-python/issues/792)) ([f54c1da](https://github.com/anthropics/anthropic-sdk-python/commit/f54c1daf964d0cca09e023bd89adf7d9c97f385d)) * **bedrock/vertex:** explicit error for unsupported messages endpoints ([c4cf816](https://github.com/anthropics/anthropic-sdk-python/commit/c4cf8164c20081cc75fefbe39db5b76be1c724e1)) * **internal:** add support for TypeAliasType ([#786](https://github.com/anthropics/anthropic-sdk-python/issues/786)) ([287ebd2](https://github.com/anthropics/anthropic-sdk-python/commit/287ebd2287ca90408999fe6be3b6f8c0295b46ef)) * **internal:** bump pydantic dependency ([#775](https://github.com/anthropics/anthropic-sdk-python/issues/775)) ([99b4d06](https://github.com/anthropics/anthropic-sdk-python/commit/99b4d06e73cdae3f2c97c304b8c0b64ec4758768)) * **internal:** bump pyright ([#769](https://github.com/anthropics/anthropic-sdk-python/issues/769)) ([81f7d70](https://github.com/anthropics/anthropic-sdk-python/commit/81f7d70fa85029f86de30ac1701ec39d01dde8f9)) * **internal:** bump pyright ([#785](https://github.com/anthropics/anthropic-sdk-python/issues/785)) ([44ab333](https://github.com/anthropics/anthropic-sdk-python/commit/44ab3339b7f3860e3a492a1784f247702bea5be0)) * **internal:** remove some duplicated imports ([#788](https://github.com/anthropics/anthropic-sdk-python/issues/788)) ([576ae9b](https://github.com/anthropics/anthropic-sdk-python/commit/576ae9b83214fd78fb02c420abfc0760270ffba8)) * **internal:** update spec ([#793](https://github.com/anthropics/anthropic-sdk-python/issues/793)) ([7cffc99](https://github.com/anthropics/anthropic-sdk-python/commit/7cffc992b17c475ceaef90868660c5a536e51624)) * **internal:** updated imports ([#789](https://github.com/anthropics/anthropic-sdk-python/issues/789)) ([d163c08](https://github.com/anthropics/anthropic-sdk-python/commit/d163c08caa9515fc5f59f284d236b16e7f0adb40)) * make the `Omit` type public ([#772](https://github.com/anthropics/anthropic-sdk-python/issues/772)) ([4ed0419](https://github.com/anthropics/anthropic-sdk-python/commit/4ed041961b59a7943b00a8e592ead0e962f36174)) * remove deprecated HTTP client options ([#777](https://github.com/anthropics/anthropic-sdk-python/issues/777)) ([3933368](https://github.com/anthropics/anthropic-sdk-python/commit/3933368e8a54d1f81c9503576e461c3d75292c39)) ### Documentation * **readme:** example snippet for client context manager ([#791](https://github.com/anthropics/anthropic-sdk-python/issues/791)) ([d0a5f0c](https://github.com/anthropics/anthropic-sdk-python/commit/d0a5f0c0568afcac5680d4c38943c8d634521c06)) * **readme:** fix http client proxies example ([#778](https://github.com/anthropics/anthropic-sdk-python/issues/778)) ([df1a549](https://github.com/anthropics/anthropic-sdk-python/commit/df1a5494d2d3be86717c344654ce54e2f97f19be)) * use latest sonnet in example snippets ([#781](https://github.com/anthropics/anthropic-sdk-python/issues/781)) ([1ad9e4f](https://github.com/anthropics/anthropic-sdk-python/commit/1ad9e4ff559f16760de15e2352a64bb2b3103071)) ## 0.40.0 (2024-11-28) Full Changelog: [v0.39.0...v0.40.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.39.0...v0.40.0) ### Features * **client:** add ._request_id property to object responses ([#743](https://github.com/anthropics/anthropic-sdk-python/issues/743)) ([9fb64a6](https://github.com/anthropics/anthropic-sdk-python/commit/9fb64a627821730fcf48662f6326d4c0f8c623ab)) ### Bug Fixes * **asyncify:** avoid hanging process under certain conditions ([#756](https://github.com/anthropics/anthropic-sdk-python/issues/756)) ([c71bba2](https://github.com/anthropics/anthropic-sdk-python/commit/c71bba2ad5248400c0142fca5e53c505f9e6d417)) * **bedrock:** correct URL encoding for model params ([#759](https://github.com/anthropics/anthropic-sdk-python/issues/759)) ([be4e73a](https://github.com/anthropics/anthropic-sdk-python/commit/be4e73a6d6ced33c887cc338c7755f5fe1697e54)) * **client:** compat with new httpx 0.28.0 release ([#765](https://github.com/anthropics/anthropic-sdk-python/issues/765)) ([de51f60](https://github.com/anthropics/anthropic-sdk-python/commit/de51f6089f7b025db5d150438caf78a615431cde)) * don't use dicts as iterables in transform ([#750](https://github.com/anthropics/anthropic-sdk-python/issues/750)) ([1f71464](https://github.com/anthropics/anthropic-sdk-python/commit/1f71464a818066687bf6c1bcae0abb991d6ed9cd)) * **types:** remove anthropic-instant-1.2 model ([#744](https://github.com/anthropics/anthropic-sdk-python/issues/744)) ([23637de](https://github.com/anthropics/anthropic-sdk-python/commit/23637de028c6c870f062fc0fbaefa9ae54a0e053)) ### Chores * **api:** update spec version ([#751](https://github.com/anthropics/anthropic-sdk-python/issues/751)) ([4ec986c](https://github.com/anthropics/anthropic-sdk-python/commit/4ec986ccfa601a78f9fff721710390c1fb4727cc)) * **ci:** remove unneeded workflow ([#742](https://github.com/anthropics/anthropic-sdk-python/issues/742)) ([472b7d3](https://github.com/anthropics/anthropic-sdk-python/commit/472b7d362c4bd32e7b32c5a10ed3d40e6821f052)) * **internal:** exclude mypy from running on tests ([#764](https://github.com/anthropics/anthropic-sdk-python/issues/764)) ([bce763a](https://github.com/anthropics/anthropic-sdk-python/commit/bce763a35f5420d0e811ff3cd4d2bf7494f11081)) * **internal:** fix compat model_dump method when warnings are passed ([#760](https://github.com/anthropics/anthropic-sdk-python/issues/760)) ([0e09236](https://github.com/anthropics/anthropic-sdk-python/commit/0e0923612bb1ce4eb18f82966055df4fb8cd348d)) * **internal:** minor formatting changes ([493020e](https://github.com/anthropics/anthropic-sdk-python/commit/493020eed859bd20e49f4ac7ec0b6c3293c7d3fd)) * remove now unused `cached-property` dep ([#762](https://github.com/anthropics/anthropic-sdk-python/issues/762)) ([b9ffefe](https://github.com/anthropics/anthropic-sdk-python/commit/b9ffefec20279705a86e4ad342597886bd7064ca)) * **tests:** adjust retry timeout values ([#736](https://github.com/anthropics/anthropic-sdk-python/issues/736)) ([27ed781](https://github.com/anthropics/anthropic-sdk-python/commit/27ed7816aa8ae32f179652ea6171dd621ef8a6b5)) * **tests:** limit array example length ([#754](https://github.com/anthropics/anthropic-sdk-python/issues/754)) ([6cab2b9](https://github.com/anthropics/anthropic-sdk-python/commit/6cab2b9237c9679ce40e319d035fb4267bca03a7)) ### Documentation * add info log level to readme ([#761](https://github.com/anthropics/anthropic-sdk-python/issues/761)) ([5966b85](https://github.com/anthropics/anthropic-sdk-python/commit/5966b855f3e58d735d8ccfe3244f549baa3a87ed)) * move comments in example snippets ([#749](https://github.com/anthropics/anthropic-sdk-python/issues/749)) ([f887930](https://github.com/anthropics/anthropic-sdk-python/commit/f887930773cbb2d5cb0d4d46ec6cd4f320d045ea)) ## 0.39.0 (2024-11-04) Full Changelog: [v0.38.0...v0.39.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.38.0...v0.39.0) ### âš  BREAKING CHANGES * **client:** remove legacy `client.count_tokens()` & `client.get_tokenizer()` methods ([#726](https://github.com/anthropics/anthropic-sdk-python/issues/726)) * This functionality has been replaced by the `client.beta.messages.count_tokens()` API which supports newer models and all content functionality, such as images and PDFs. ### Features * **api:** add new haiku model ([#731](https://github.com/anthropics/anthropic-sdk-python/issues/731)) ([77eaaf9](https://github.com/anthropics/anthropic-sdk-python/commit/77eaaf9c76f9b267706c830a5f7c1d81df6013d9)) * **project:** drop support for Python 3.7 ([#729](https://github.com/anthropics/anthropic-sdk-python/issues/729)) ([7f897e2](https://github.com/anthropics/anthropic-sdk-python/commit/7f897e253ae09e6a85fe64ba8004c2c3a8133e4e)) ### Bug Fixes * don't use dicts as iterables in transform ([#724](https://github.com/anthropics/anthropic-sdk-python/issues/724)) ([62bb863](https://github.com/anthropics/anthropic-sdk-python/commit/62bb8636a3d7156bc0caab5f574b1fa72445cead)) * support json safe serialization for basemodel subclasses ([#727](https://github.com/anthropics/anthropic-sdk-python/issues/727)) ([5be855e](https://github.com/anthropics/anthropic-sdk-python/commit/5be855e20f40042f59e839c7747dd994dc88c456)) * **types:** add missing token-counting-2024-11-01 ([#722](https://github.com/anthropics/anthropic-sdk-python/issues/722)) ([c549736](https://github.com/anthropics/anthropic-sdk-python/commit/c5497360a385f5dbaa5ab775bc19a0d7eee713bc)) ### Documentation * **readme:** mention new token counting endpoint ([#728](https://github.com/anthropics/anthropic-sdk-python/issues/728)) ([72a4636](https://github.com/anthropics/anthropic-sdk-python/commit/72a4636a7798170d69e7551ba58a0213d82d1711)) ### Refactors * **client:** remove legacy `client.count_tokens()` method ([#726](https://github.com/anthropics/anthropic-sdk-python/issues/726)) ([14e4244](https://github.com/anthropics/anthropic-sdk-python/commit/14e4244752b656cedfe7d160088e9744d07470a1)) ## 0.38.0 (2024-11-01) Full Changelog: [v0.37.1...v0.38.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.37.1...v0.38.0) ### Features * **api:** add message token counting & PDFs support ([#721](https://github.com/anthropics/anthropic-sdk-python/issues/721)) ([e4856dd](https://github.com/anthropics/anthropic-sdk-python/commit/e4856dd6be698e797eaee2d6a669a6aaa6719e7d)) ### Bug Fixes * **count_tokens:** correctly set beta header ([e5b4b54](https://github.com/anthropics/anthropic-sdk-python/commit/e5b4b54e3ea1b4fc2d947d45df17996f66900387)) * **types:** add missing token-counting-2024-11-01 ([1897883](https://github.com/anthropics/anthropic-sdk-python/commit/1897883d6332bd0ad10cf13ad09e30563c3e7232)) ### Chores * **internal:** bump mypy ([#720](https://github.com/anthropics/anthropic-sdk-python/issues/720)) ([fe8d19e](https://github.com/anthropics/anthropic-sdk-python/commit/fe8d19e265c57fa9e34a09e46e808322e70c721d)) * **internal:** bump pytest to v8 & pydantic ([#716](https://github.com/anthropics/anthropic-sdk-python/issues/716)) ([00fe1f8](https://github.com/anthropics/anthropic-sdk-python/commit/00fe1f8b0c9c1312c7f1e62ce6ef9c5c56478ede)) * **internal:** update spec version ([#712](https://github.com/anthropics/anthropic-sdk-python/issues/712)) ([f71b0f5](https://github.com/anthropics/anthropic-sdk-python/commit/f71b0f5e54aab20b93cd13609151aa833583145a)) * **tests:** move lazy tokenizer test outside of pytest ([d8f2402](https://github.com/anthropics/anthropic-sdk-python/commit/d8f24023c7110b89528b7b37ddf9c1e6630562c4)) ## 0.37.1 (2024-10-22) Full Changelog: [v0.37.0...v0.37.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.37.0...v0.37.1) ### Bug Fixes * **bedrock:** correct handling of messages beta ([#711](https://github.com/anthropics/anthropic-sdk-python/issues/711)) ([4cba32b](https://github.com/anthropics/anthropic-sdk-python/commit/4cba32b41e82377e155612e05c847baf2ca166d0)) * **vertex:** use correct beta url ([b76db5c](https://github.com/anthropics/anthropic-sdk-python/commit/b76db5c90a9b22d4078a15ac6844a4f75dcbc857)) ## 0.37.0 (2024-10-22) Full Changelog: [v0.36.2...v0.37.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.36.2...v0.37.0) ### Features * **api:** add new model and `computer-use-2024-10-22` beta ([dd93d87](https://github.com/anthropics/anthropic-sdk-python/commit/dd93d872dd00a52b5bd65f84451fc9d368692cde)) * **bedrock:** add messages beta ([2566c93](https://github.com/anthropics/anthropic-sdk-python/commit/2566c93a7d7a861136ebdfde0eb90287977d43d1)) * **vertex:** add messages beta ([0d1f1a6](https://github.com/anthropics/anthropic-sdk-python/commit/0d1f1a663a7bd43f1f524ddaad1d6e18d6d68a61)) ### Bug Fixes * **client/async:** correctly retry in all cases ([#704](https://github.com/anthropics/anthropic-sdk-python/issues/704)) ([ee6febc](https://github.com/anthropics/anthropic-sdk-python/commit/ee6febc4b5c58db9aed835b021e476b46f68033e)) ### Chores * **api:** add title ([#703](https://github.com/anthropics/anthropic-sdk-python/issues/703)) ([a046817](https://github.com/anthropics/anthropic-sdk-python/commit/a046817e9181d35f145249eeb070cfbfd2c36901)) * **internal:** bump ruff dependency ([#700](https://github.com/anthropics/anthropic-sdk-python/issues/700)) ([d5bf9e1](https://github.com/anthropics/anthropic-sdk-python/commit/d5bf9e1f88486552dfba1409cfc699994a1e9aab)) * **internal:** remove unused black config ([#705](https://github.com/anthropics/anthropic-sdk-python/issues/705)) ([3259eb0](https://github.com/anthropics/anthropic-sdk-python/commit/3259eb0a740e65ec649da41667a703d0a4ec1eee)) * **internal:** update spec ([#706](https://github.com/anthropics/anthropic-sdk-python/issues/706)) ([6ab0ce9](https://github.com/anthropics/anthropic-sdk-python/commit/6ab0ce9acf3fdfb217c3885198f88a9ece0969af)) ## 0.36.2 (2024-10-17) Full Changelog: [v0.36.1...v0.36.2](https://github.com/anthropics/anthropic-sdk-python/compare/v0.36.1...v0.36.2) ### Bug Fixes * **types:** remove misleading betas TypedDict property for the Batch API ([#697](https://github.com/anthropics/anthropic-sdk-python/issues/697)) ([e1b9e31](https://github.com/anthropics/anthropic-sdk-python/commit/e1b9e311644103466904fdce78469380d971ccad)) ### Chores * **internal:** update test syntax ([#699](https://github.com/anthropics/anthropic-sdk-python/issues/699)) ([a836157](https://github.com/anthropics/anthropic-sdk-python/commit/a836157d1c0cefc452f5f27fe90eea3b4ff687d2)) ## 0.36.1 (2024-10-15) Full Changelog: [v0.36.0...v0.36.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.36.0...v0.36.1) ### Bug Fixes * allow header params to override default headers ([#690](https://github.com/anthropics/anthropic-sdk-python/issues/690)) ([56f195f](https://github.com/anthropics/anthropic-sdk-python/commit/56f195ff1a67f2ba6e546ad897cbb0fe39f36a3b)) * **beta:** merge betas param with the default value ([#695](https://github.com/anthropics/anthropic-sdk-python/issues/695)) ([f52eac9](https://github.com/anthropics/anthropic-sdk-python/commit/f52eac9357c0c496f1307def2628376d8a36e5ba)) ### Chores * **internal:** update spec URL ([#694](https://github.com/anthropics/anthropic-sdk-python/issues/694)) ([1b437cc](https://github.com/anthropics/anthropic-sdk-python/commit/1b437cc40f2e02e59df06744cc1458ee0cae202a)) ## 0.36.0 (2024-10-08) Full Changelog: [v0.35.0...v0.36.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.35.0...v0.36.0) ### Features * **api:** add message batches api ([cd1ffcb](https://github.com/anthropics/anthropic-sdk-python/commit/cd1ffcb5e506c62e82e6f1365718949840724b9a)) ### Bug Fixes * **client:** avoid OverflowError with very large retry counts ([#676](https://github.com/anthropics/anthropic-sdk-python/issues/676)) ([93d6eeb](https://github.com/anthropics/anthropic-sdk-python/commit/93d6eeb80e63424b8a97f949d5354052b6b16cf4)) ### Chores * add repr to PageInfo class ([#678](https://github.com/anthropics/anthropic-sdk-python/issues/678)) ([53e87e8](https://github.com/anthropics/anthropic-sdk-python/commit/53e87e8abed82fe90fb2d877f69a2cc695662e86)) ### Refactors * **types:** improve metadata type names ([#683](https://github.com/anthropics/anthropic-sdk-python/issues/683)) ([59f2088](https://github.com/anthropics/anthropic-sdk-python/commit/59f208855039bb7b31266ae5e12d7454ecd69f3b)) * **types:** improve metadata types ([#682](https://github.com/anthropics/anthropic-sdk-python/issues/682)) ([e037d1c](https://github.com/anthropics/anthropic-sdk-python/commit/e037d1c310a6487e4f94a751232399e75f10b46d)) * **types:** improve tool type names ([#679](https://github.com/anthropics/anthropic-sdk-python/issues/679)) ([f6f3afe](https://github.com/anthropics/anthropic-sdk-python/commit/f6f3afe2e26ba004a24f7c3bdd36c7b4c1ae4697)) * **types:** improve tool type names ([#680](https://github.com/anthropics/anthropic-sdk-python/issues/680)) ([fe2e417](https://github.com/anthropics/anthropic-sdk-python/commit/fe2e4178dbdea42e84fd925e9e259fef6134c3d3)) ## 0.35.0 (2024-10-04) Full Changelog: [v0.34.2...v0.35.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.34.2...v0.35.0) ### Features * **api:** support disabling parallel tool use ([#674](https://github.com/anthropics/anthropic-sdk-python/issues/674)) ([9079a99](https://github.com/anthropics/anthropic-sdk-python/commit/9079a99fffe5cf7bc91f052ed46b568e55792abf)) * **bedrock:** add `profile` argument to client ([#648](https://github.com/anthropics/anthropic-sdk-python/issues/648)) ([6ea5fce](https://github.com/anthropics/anthropic-sdk-python/commit/6ea5fce3b3a4d1ef4d5d3bbce8e27ea11e6dae72)) * **client:** allow overriding retry count header ([#670](https://github.com/anthropics/anthropic-sdk-python/issues/670)) ([1fb081f](https://github.com/anthropics/anthropic-sdk-python/commit/1fb081fa2005ad30d78a97755f14f81cbcfe28ab)) * **client:** send retry count header ([#664](https://github.com/anthropics/anthropic-sdk-python/issues/664)) ([17c26d5](https://github.com/anthropics/anthropic-sdk-python/commit/17c26d5761b3ee686525f43b22ab6d5e40fc90b1)) ### Bug Fixes * **client:** handle domains with underscores ([#663](https://github.com/anthropics/anthropic-sdk-python/issues/663)) ([84ad451](https://github.com/anthropics/anthropic-sdk-python/commit/84ad451bf1fa9ddff1f409472e8b63ae7678aa83)) * **types:** correctly mark stream discriminator as optional ([#657](https://github.com/anthropics/anthropic-sdk-python/issues/657)) ([2386f98](https://github.com/anthropics/anthropic-sdk-python/commit/2386f983593613034e6ca106be6a0cf95009ea4c)) ### Chores * add docstrings to raw response properties ([#654](https://github.com/anthropics/anthropic-sdk-python/issues/654)) ([35e6cf7](https://github.com/anthropics/anthropic-sdk-python/commit/35e6cf7c39d715181fb68f8fea6b835bf5d2085d)) * **internal:** add support for parsing bool response content ([#675](https://github.com/anthropics/anthropic-sdk-python/issues/675)) ([0bbc0a3](https://github.com/anthropics/anthropic-sdk-python/commit/0bbc0a365b9d64be93cfc8e6b992df95d83c06d7)) * **internal:** bump pyright / mypy version ([#662](https://github.com/anthropics/anthropic-sdk-python/issues/662)) ([c03a71f](https://github.com/anthropics/anthropic-sdk-python/commit/c03a71f71af845eef0b38ff29cdbaa444464fc6e)) * **internal:** bump ruff ([#660](https://github.com/anthropics/anthropic-sdk-python/issues/660)) ([0a34018](https://github.com/anthropics/anthropic-sdk-python/commit/0a34018057f818bf11ec0019ed1e9f413919b682)) * **internal:** update pydantic v1 compat helpers ([#666](https://github.com/anthropics/anthropic-sdk-python/issues/666)) ([ee8e2bd](https://github.com/anthropics/anthropic-sdk-python/commit/ee8e2bdd66b017ef87431ac8ff0b550b18548a3d)) * **internal:** use `typing_extensions.overload` instead of `typing` ([#667](https://github.com/anthropics/anthropic-sdk-python/issues/667)) ([153361d](https://github.com/anthropics/anthropic-sdk-python/commit/153361d4f24cc3497bd62a0a403007c889a8ed51)) * pyproject.toml formatting changes ([#650](https://github.com/anthropics/anthropic-sdk-python/issues/650)) ([4c229dc](https://github.com/anthropics/anthropic-sdk-python/commit/4c229dcdddb59785469390f330f82763d052cf4d)) ### Documentation * fix typo in fenced code block language ([#673](https://github.com/anthropics/anthropic-sdk-python/issues/673)) ([a03414e](https://github.com/anthropics/anthropic-sdk-python/commit/a03414e2d84c76db2cdf5e7ef2d04fef3b74b01a)) * improve and reference contributing documentation ([#672](https://github.com/anthropics/anthropic-sdk-python/issues/672)) ([5bd9690](https://github.com/anthropics/anthropic-sdk-python/commit/5bd96900d56338336efa37156ea144df7b69c624)) * **readme:** add section on determining installed version ([#655](https://github.com/anthropics/anthropic-sdk-python/issues/655)) ([5898f42](https://github.com/anthropics/anthropic-sdk-python/commit/5898f42ec2b794bcc26c98336768a10d6efed44f)) * update CONTRIBUTING.md ([#659](https://github.com/anthropics/anthropic-sdk-python/issues/659)) ([2df25bf](https://github.com/anthropics/anthropic-sdk-python/commit/2df25bf6d65a39fea6f526b692298e25667b1148)) ## 0.34.2 (2024-09-04) Full Changelog: [v0.34.1...v0.34.2](https://github.com/anthropics/anthropic-sdk-python/compare/v0.34.1...v0.34.2) ### Chores * **api:** deprecate claude-1 model ([eab07dc](https://github.com/anthropics/anthropic-sdk-python/commit/eab07dc1984ea20918bb0d902108a1ce4646a1e0)) * **ci:** also run pydantic v1 tests ([#644](https://github.com/anthropics/anthropic-sdk-python/issues/644)) ([c61fe89](https://github.com/anthropics/anthropic-sdk-python/commit/c61fe899e79f21691c7a19d40f1bc397b3f3f82d)) ## 0.34.1 (2024-08-19) Full Changelog: [v0.34.0...v0.34.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.34.0...v0.34.1) ### Chores * **ci:** add CODEOWNERS file ([#639](https://github.com/anthropics/anthropic-sdk-python/issues/639)) ([33001cc](https://github.com/anthropics/anthropic-sdk-python/commit/33001ccf80f6ec2ac43b74f5f41034ec6a12552b)) * **client:** fix parsing union responses when non-json is returned ([#643](https://github.com/anthropics/anthropic-sdk-python/issues/643)) ([45be91d](https://github.com/anthropics/anthropic-sdk-python/commit/45be91dbcc2789a71a048a34f1f23977b9829818)) * **docs/api:** update prompt caching helpers ([6a55aee](https://github.com/anthropics/anthropic-sdk-python/commit/6a55aeeaca83ade0adc18eae0f8682558769d5ff)) * **internal:** use different 32bit detection method ([#640](https://github.com/anthropics/anthropic-sdk-python/issues/640)) ([d6b2b63](https://github.com/anthropics/anthropic-sdk-python/commit/d6b2b630613f7c5f01fc3cd005a055989e7d8e71)) ## 0.34.0 (2024-08-14) Full Changelog: [v0.33.1...v0.34.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.33.1...v0.34.0) ### Features * **api:** add prompt caching beta ([3978411](https://github.com/anthropics/anthropic-sdk-python/commit/397841125164a2420d5abf8f45d47f2467e36cd9)) * **client:** add streaming helpers for prompt caching ([98a0a7b](https://github.com/anthropics/anthropic-sdk-python/commit/98a0a7b9c679539c98d212b12c0a9a950fd6371d)) ### Chores * **examples:** minor formatting changes ([#633](https://github.com/anthropics/anthropic-sdk-python/issues/633)) ([20487ea](https://github.com/anthropics/anthropic-sdk-python/commit/20487ea0080969511e7c41f199387b87a84f6ab4)) ## 0.33.1 (2024-08-12) Full Changelog: [v0.33.0...v0.33.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.33.0...v0.33.1) ### Chores * **ci:** bump prism mock server version ([#630](https://github.com/anthropics/anthropic-sdk-python/issues/630)) ([29545ee](https://github.com/anthropics/anthropic-sdk-python/commit/29545eee2e7bfdfe73b590d9301aa68bbf2c361d)) * **internal:** ensure package is importable in lint cmd ([#632](https://github.com/anthropics/anthropic-sdk-python/issues/632)) ([d685824](https://github.com/anthropics/anthropic-sdk-python/commit/d685824b2c080bd1b17f677f4af422b5cb0e7ed5)) ## 0.33.0 (2024-08-09) Full Changelog: [v0.32.0...v0.33.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.32.0...v0.33.0) ### Features * **client:** add `retries_taken` to raw response class ([43fb587](https://github.com/anthropics/anthropic-sdk-python/commit/43fb5874b0a2398221d1f1d0fea316faca9f6484)) ### Chores * **internal:** bump pyright ([#622](https://github.com/anthropics/anthropic-sdk-python/issues/622)) ([9480109](https://github.com/anthropics/anthropic-sdk-python/commit/9480109c380ff571487429d5f50f50e23947d788)) * **internal:** bump ruff version ([#625](https://github.com/anthropics/anthropic-sdk-python/issues/625)) ([b1a4e7b](https://github.com/anthropics/anthropic-sdk-python/commit/b1a4e7b9a8c17184038d1816ff08619cb03f6296)) * **internal:** test updates ([#624](https://github.com/anthropics/anthropic-sdk-python/issues/624)) ([2cea1f5](https://github.com/anthropics/anthropic-sdk-python/commit/2cea1f52bad2fb6b8f0705fd672f75d8a6281ba0)) * **internal:** update pydantic compat helper function ([#627](https://github.com/anthropics/anthropic-sdk-python/issues/627)) ([dc18ee0](https://github.com/anthropics/anthropic-sdk-python/commit/dc18ee0af5a86429ee8bcc9d5c186493f8d5c622)) * **internal:** updates ([#629](https://github.com/anthropics/anthropic-sdk-python/issues/629)) ([d6357a6](https://github.com/anthropics/anthropic-sdk-python/commit/d6357a6172a38d7cf5ab51d9d7b699d44d2adc21)) * **internal:** use `TypeAlias` marker for type assignments ([#621](https://github.com/anthropics/anthropic-sdk-python/issues/621)) ([a4bff9c](https://github.com/anthropics/anthropic-sdk-python/commit/a4bff9cee99d3ee2083426ec41b40bdcf70d6b4f)) * sync openapi version ([#617](https://github.com/anthropics/anthropic-sdk-python/issues/617)) ([9c0ad95](https://github.com/anthropics/anthropic-sdk-python/commit/9c0ad95b530f1fbd2293a15dcce7f583a982aad0)) * sync openapi version ([#620](https://github.com/anthropics/anthropic-sdk-python/issues/620)) ([0a3f3fa](https://github.com/anthropics/anthropic-sdk-python/commit/0a3f3fa8d89f90f321c27b0cb8c4187b68161fc5)) * sync openapi version ([#628](https://github.com/anthropics/anthropic-sdk-python/issues/628)) ([cfad41f](https://github.com/anthropics/anthropic-sdk-python/commit/cfad41f8a36836060d1b2bba0f32ee291ff8df05)) ## 0.32.0 (2024-07-29) Full Changelog: [v0.31.2...v0.32.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.31.2...v0.32.0) ### Features * add back compat alias for InputJsonDelta ([25a5b6c](https://github.com/anthropics/anthropic-sdk-python/commit/25a5b6c81ffb5996ef697aab22a22d8be5751bc1)) ### Bug Fixes * change signatures for the stream function ([c9eb11b](https://github.com/anthropics/anthropic-sdk-python/commit/c9eb11b1f9656202ee88e9869e59160bc37f5434)) * **client:** correctly apply client level timeout for messages ([#615](https://github.com/anthropics/anthropic-sdk-python/issues/615)) ([5f8d88f](https://github.com/anthropics/anthropic-sdk-python/commit/5f8d88f6fcc2ba05cd9fc6f8ae7aa8c61dc6b0d0)) ### Chores * **docs:** document how to do per-request http client customization ([#603](https://github.com/anthropics/anthropic-sdk-python/issues/603)) ([5161f62](https://github.com/anthropics/anthropic-sdk-python/commit/5161f626a0bec757b96217dc0f81e8908546f29a)) * **internal:** add type construction helper ([#613](https://github.com/anthropics/anthropic-sdk-python/issues/613)) ([5e36940](https://github.com/anthropics/anthropic-sdk-python/commit/5e36940a42e401c3f0c1e42aa248d431fdf7192c)) * sync spec ([#605](https://github.com/anthropics/anthropic-sdk-python/issues/605)) ([6b7707f](https://github.com/anthropics/anthropic-sdk-python/commit/6b7707f62788fca2e166209e82935a2a2fa8204a)) * **tests:** update prism version ([#607](https://github.com/anthropics/anthropic-sdk-python/issues/607)) ([1797dc6](https://github.com/anthropics/anthropic-sdk-python/commit/1797dc6139ffaca6436ed897972471e67ba1b828)) ### Refactors * extract model out to a named type and rename partialjson ([#612](https://github.com/anthropics/anthropic-sdk-python/issues/612)) ([c53efc7](https://github.com/anthropics/anthropic-sdk-python/commit/c53efc786fa95831a398f37740a81b42f7b64c94)) ## 0.31.2 (2024-07-17) Full Changelog: [v0.31.1...v0.31.2](https://github.com/anthropics/anthropic-sdk-python/compare/v0.31.1...v0.31.2) ### Bug Fixes * **vertex:** also refresh auth if there is no token ([4a8d02d](https://github.com/anthropics/anthropic-sdk-python/commit/4a8d02d0616c04a2acc31a3179b7d50093d6371e)) * **vertex:** correct request options in retries ([460547b](https://github.com/anthropics/anthropic-sdk-python/commit/460547b7e6bafa4044127760946d141d1e49131b)) ### Chores * **docs:** minor update to formatting of API link in README ([#594](https://github.com/anthropics/anthropic-sdk-python/issues/594)) ([113b6ac](https://github.com/anthropics/anthropic-sdk-python/commit/113b6ac65de2a670b0d957d11d48b060106150d3)) * **internal:** update formatting ([#597](https://github.com/anthropics/anthropic-sdk-python/issues/597)) ([565dfcd](https://github.com/anthropics/anthropic-sdk-python/commit/565dfcd4610c26b598f6c72e9182e8c60bffc2a0)) * **tests:** faster bedrock retry tests ([4ff067f](https://github.com/anthropics/anthropic-sdk-python/commit/4ff067f48e8e177ebdb8f06d6a4a0ffe9a096a8b)) ## 0.31.1 (2024-07-15) Full Changelog: [v0.31.0...v0.31.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.31.0...v0.31.1) ### Bug Fixes * **bedrock:** correct request options for retries ([#593](https://github.com/anthropics/anthropic-sdk-python/issues/593)) ([f68c81d](https://github.com/anthropics/anthropic-sdk-python/commit/f68c81d072fceb46d4c0d8ee62cf274eeea99415)) ### Chores * **ci:** also run workflows for PRs targeting `next` ([#587](https://github.com/anthropics/anthropic-sdk-python/issues/587)) ([f7e49f2](https://github.com/anthropics/anthropic-sdk-python/commit/f7e49f2f2ceb62cccd6961fc1bd799655ccd83ab)) * **internal:** minor changes to tests ([#591](https://github.com/anthropics/anthropic-sdk-python/issues/591)) ([fabd591](https://github.com/anthropics/anthropic-sdk-python/commit/fabd5910f2e769b8bfbeaaa8b65ca8383b4954e3)) * **internal:** minor formatting changes ([a71927b](https://github.com/anthropics/anthropic-sdk-python/commit/a71927b7c7cff4e83eb485d3b0eef928a18acef6)) * **internal:** minor import restructuring ([#588](https://github.com/anthropics/anthropic-sdk-python/issues/588)) ([1d9db4f](https://github.com/anthropics/anthropic-sdk-python/commit/1d9db4f6c1393c3879e83e1a3e1d1b4fedc33b5a)) * **internal:** minor options / compat functions updates ([#592](https://github.com/anthropics/anthropic-sdk-python/issues/592)) ([d41a880](https://github.com/anthropics/anthropic-sdk-python/commit/d41a8807057958d4505e16325e4a06359a760260)) * **internal:** update mypy ([#584](https://github.com/anthropics/anthropic-sdk-python/issues/584)) ([0a0edce](https://github.com/anthropics/anthropic-sdk-python/commit/0a0edce53e9eebd47770e71493302527e7f43751)) ## 0.31.0 (2024-07-10) Full Changelog: [v0.30.1...v0.31.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.30.1...v0.31.0) ### Features * **client:** make request-id header more accessible ([#581](https://github.com/anthropics/anthropic-sdk-python/issues/581)) ([130d470](https://github.com/anthropics/anthropic-sdk-python/commit/130d470fc624a25defb9d8e787462b77bdc0aad5)) * **vertex:** add copy and with_options ([#578](https://github.com/anthropics/anthropic-sdk-python/issues/578)) ([fcd425f](https://github.com/anthropics/anthropic-sdk-python/commit/fcd425f724fee45195118aa218bd5c51fb9abed0)) ### Bug Fixes * **client:** always respect content-type multipart/form-data if provided ([#574](https://github.com/anthropics/anthropic-sdk-python/issues/574)) ([6051763](https://github.com/anthropics/anthropic-sdk-python/commit/6051763d886aa7107389d8b8aeacf74d296eed3d)) * **streaming/messages:** more robust event type construction ([#576](https://github.com/anthropics/anthropic-sdk-python/issues/576)) ([98e2075](https://github.com/anthropics/anthropic-sdk-python/commit/98e2075869d816cd85af1a0588bd27719eff02a4)) * **types:** allow arbitrary types in image block param ([#582](https://github.com/anthropics/anthropic-sdk-python/issues/582)) ([ebd6590](https://github.com/anthropics/anthropic-sdk-python/commit/ebd659014b63b51fa2f67fe88ef3fc9922be830d)) * Updated doc typo ([17be06b](https://github.com/anthropics/anthropic-sdk-python/commit/17be06bf3e39eff9de588d99cd59fa509c5ee6a6)) * **vertex:** avoid credentials refresh on every request ([#575](https://github.com/anthropics/anthropic-sdk-python/issues/575)) ([37bd433](https://github.com/anthropics/anthropic-sdk-python/commit/37bd4337828f3efa14b194fa3025638229129416)) ### Chores * **ci:** update rye to v0.35.0 ([#577](https://github.com/anthropics/anthropic-sdk-python/issues/577)) ([e271d69](https://github.com/anthropics/anthropic-sdk-python/commit/e271d694babfb4bcb506064aa353ee29a8394c1d)) * **internal:** add helper method for constructing `BaseModel`s ([#572](https://github.com/anthropics/anthropic-sdk-python/issues/572)) ([8e626ac](https://github.com/anthropics/anthropic-sdk-python/commit/8e626ac7c88bab413bc1e2d83b7556aa4a44fb63)) * **internal:** fix formatting ([a912917](https://github.com/anthropics/anthropic-sdk-python/commit/a912917686d6e4a46d192abf002ac69357b1d955)) * **internal:** minor request options handling changes ([#580](https://github.com/anthropics/anthropic-sdk-python/issues/580)) ([d1dcf42](https://github.com/anthropics/anthropic-sdk-python/commit/d1dcf427ea78f57dd267d891c276b03d4010de78)) ## 0.30.1 (2024-07-01) Full Changelog: [v0.30.0...v0.30.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.30.0...v0.30.1) ### Bug Fixes * **build:** include more files in sdist builds ([#559](https://github.com/anthropics/anthropic-sdk-python/issues/559)) ([9170d08](https://github.com/anthropics/anthropic-sdk-python/commit/9170d08e056ecb33f1441f50b8407a1c60c45d94)) ### Chores * **deps:** bump anyio to v4.4.0 ([#562](https://github.com/anthropics/anthropic-sdk-python/issues/562)) ([70fc936](https://github.com/anthropics/anthropic-sdk-python/commit/70fc9361848e4825f8036da2b76a189d602e0baf)) * gitignore test server logs ([#567](https://github.com/anthropics/anthropic-sdk-python/issues/567)) ([f7b9283](https://github.com/anthropics/anthropic-sdk-python/commit/f7b928386b9f6dfdea6842ce729024afdc55da3f)) * **internal:** add reflection helper function ([#565](https://github.com/anthropics/anthropic-sdk-python/issues/565)) ([9483573](https://github.com/anthropics/anthropic-sdk-python/commit/948357378f2234e7ddc3843c0427cfa0b9914a21)) * **internal:** add rich as a dev dependency ([#568](https://github.com/anthropics/anthropic-sdk-python/issues/568)) ([07903ac](https://github.com/anthropics/anthropic-sdk-python/commit/07903acb9388ce6a3c35058880c89e1275aab1e3)) ## 0.30.0 (2024-06-26) Full Changelog: [v0.29.2...v0.30.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.29.2...v0.30.0) ### Features * **vertex:** add credentials argument ([#542](https://github.com/anthropics/anthropic-sdk-python/issues/542)) ([3bfb2ea](https://github.com/anthropics/anthropic-sdk-python/commit/3bfb2eaf59410053870c7a598bef6404f2201145)) ## 0.29.2 (2024-06-26) Full Changelog: [v0.29.1...v0.29.2](https://github.com/anthropics/anthropic-sdk-python/compare/v0.29.1...v0.29.2) ### Bug Fixes * temporarily patch upstream version to fix broken release flow ([#555](https://github.com/anthropics/anthropic-sdk-python/issues/555)) ([5471710](https://github.com/anthropics/anthropic-sdk-python/commit/54717101f3844791bdde8b9b76f47abf04c6a971)) ## 0.29.1 (2024-06-25) Full Changelog: [v0.29.0...v0.29.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.29.0...v0.29.1) ### Bug Fixes * **api:** add string to tool result block ([#554](https://github.com/anthropics/anthropic-sdk-python/issues/554)) ([f283b4e](https://github.com/anthropics/anthropic-sdk-python/commit/f283b4eb9e4f118bb4ada38479747b22dd5282fa)) * **docs:** fix link to advanced python httpx docs ([#550](https://github.com/anthropics/anthropic-sdk-python/issues/550)) ([474ff7c](https://github.com/anthropics/anthropic-sdk-python/commit/474ff7cad99039f3539a787ec535b5b13e2832a9)) ## 0.29.0 (2024-06-20) Full Changelog: [v0.28.1...v0.29.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.28.1...v0.29.0) ### Features * **api:** add new claude-3-5-sonnet-20240620 model ([#545](https://github.com/anthropics/anthropic-sdk-python/issues/545)) ([5ea6b18](https://github.com/anthropics/anthropic-sdk-python/commit/5ea6b182715cd355cc405554b81f3d0f725486f6)) ### Bug Fixes * **client/async:** avoid blocking io call for platform headers ([#544](https://github.com/anthropics/anthropic-sdk-python/issues/544)) ([3c2b75f](https://github.com/anthropics/anthropic-sdk-python/commit/3c2b75fac662e48effc8ec032266d966e548007d)) ### Chores * **internal:** add a `default_query` method ([#540](https://github.com/anthropics/anthropic-sdk-python/issues/540)) ([0253ebc](https://github.com/anthropics/anthropic-sdk-python/commit/0253ebc9cda491ab909cc752d719e797086691ed)) ## 0.28.1 (2024-06-14) Full Changelog: [v0.28.0...v0.28.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.28.0...v0.28.1) ### Documentation * **readme:** tool use is no longer in beta ([d2be3c0](https://github.com/anthropics/anthropic-sdk-python/commit/d2be3c0438429b6521fc49f5a5ff17fae71fb589)) ## 0.28.0 (2024-05-30) Full Changelog: [v0.27.0...v0.28.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.27.0...v0.28.0) ### âš  BREAKING CHANGES * **streaming:** remove old event_handler API ([#532](https://github.com/anthropics/anthropic-sdk-python/issues/532)) ### Refactors * **streaming:** remove old event_handler API ([#532](https://github.com/anthropics/anthropic-sdk-python/issues/532)) ([d9acfd4](https://github.com/anthropics/anthropic-sdk-python/commit/d9acfd427e3d7d8c6bc3d6ed8994194a07ed6a92)) ## 0.27.0 (2024-05-30) Full Changelog: [v0.26.2...v0.27.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.26.2...v0.27.0) ### Features * **api:** tool use is GA and available on 3P ([#530](https://github.com/anthropics/anthropic-sdk-python/issues/530)) ([ad7adbd](https://github.com/anthropics/anthropic-sdk-python/commit/ad7adbd2a732db98665333c27065ff4f4c946f15)) * **streaming/messages:** refactor to event iterator structure ([997af69](https://github.com/anthropics/anthropic-sdk-python/commit/997af696a713a604d4146f36caf91397ba488e33)) * **streaming/tools:** refactor to event iterator structure ([bdcc283](https://github.com/anthropics/anthropic-sdk-python/commit/bdcc28303206fde2da01296cdae553c1e8efb60a)) * **streaming:** add tools support ([9f00950](https://github.com/anthropics/anthropic-sdk-python/commit/9f00950b81d388f14027c48aca1ca3c044b93a03)) ### Bug Fixes * **beta:** streaming breakage due to breaking change in dependency ([afe3c87](https://github.com/anthropics/anthropic-sdk-python/commit/afe3c8726576cdc7e0503707f53fa9a80caed962)) ### Chores * add missing __all__ definitions ([#526](https://github.com/anthropics/anthropic-sdk-python/issues/526)) ([5021787](https://github.com/anthropics/anthropic-sdk-python/commit/5021787caeda8a38775c69449a5794b1072dbfe5)) * **examples:** update tools ([56edecc](https://github.com/anthropics/anthropic-sdk-python/commit/56edecc2de1e943d6ca09a788c4fabac5978ea2d)) * **formatting:** misc fixups ([fbad5a0](https://github.com/anthropics/anthropic-sdk-python/commit/fbad5a0e0d7f4dbeeffa8a038600c9acb88002fc)) * **internal:** fix lint issues in tests ([d857640](https://github.com/anthropics/anthropic-sdk-python/commit/d857640c1e30b580e7e94e034a1fbc07f655acc6)) * **internal:** update bootstrap script ([#527](https://github.com/anthropics/anthropic-sdk-python/issues/527)) ([93ae152](https://github.com/anthropics/anthropic-sdk-python/commit/93ae1528c0404631f32c49341032ca0d11314b80)) * **internal:** update some references to rye-up.com ([00e34e7](https://github.com/anthropics/anthropic-sdk-python/commit/00e34e7fbbb3a797d55bb94c07d551ad083dc7d9)) * **tests:** ensure messages.create() and messages.stream() stay in sync ([52bd67b](https://github.com/anthropics/anthropic-sdk-python/commit/52bd67b041283adeee662355d8df297ca4b1d560)) ### Documentation * **helpers:** mention input json event ([02d482c](https://github.com/anthropics/anthropic-sdk-python/commit/02d482c03c039bc635c4d35e04cebe4670e1762c)) * **helpers:** update for new event iterator ([26f9533](https://github.com/anthropics/anthropic-sdk-python/commit/26f9533df19ee3da55c590238eba745051cccf6c)) ### Refactors * **api:** add Raw prefix to API stream event type names ([#529](https://github.com/anthropics/anthropic-sdk-python/issues/529)) ([bb62980](https://github.com/anthropics/anthropic-sdk-python/commit/bb629806887de6cd3e5d517af4d9615f40076542)) ## 0.26.2 (2024-05-27) Full Changelog: [v0.26.1...v0.26.2](https://github.com/anthropics/anthropic-sdk-python/compare/v0.26.1...v0.26.2) ### Bug Fixes * **vertex:** don't error if project_id couldn't be loaded if it was already explicitly given ([#513](https://github.com/anthropics/anthropic-sdk-python/issues/513)) ([e7159d8](https://github.com/anthropics/anthropic-sdk-python/commit/e7159d87b207592eff364c1d75bab348dd414257)) ### Chores * **ci:** update rye install location ([#516](https://github.com/anthropics/anthropic-sdk-python/issues/516)) ([a6e347a](https://github.com/anthropics/anthropic-sdk-python/commit/a6e347a2c4aa75d00ee3ada3dfa707a080d890b6)) * **ci:** update rye install location ([#518](https://github.com/anthropics/anthropic-sdk-python/issues/518)) ([5122420](https://github.com/anthropics/anthropic-sdk-python/commit/51224208a732136caeb30d839685a91d7a26beda)) * **internal:** bump pyright ([196e4b0](https://github.com/anthropics/anthropic-sdk-python/commit/196e4b06cb4794a06d813b4e59dd8c5fbb61d71d)) * **internal:** remove unused __events stream property ([472b831](https://github.com/anthropics/anthropic-sdk-python/commit/472b831a552e7ebe20a9d503b129d8c1e1cef0c8)) * **internal:** restructure streaming implementation to use composition ([b1a1c03](https://github.com/anthropics/anthropic-sdk-python/commit/b1a1c0354a9aca450a7d512fdbdeb59c0ead688a)) * **messages:** add back-compat for isinstance() checks ([7794bcb](https://github.com/anthropics/anthropic-sdk-python/commit/7794bcb680300249cd9be48562ce190eed8b9cff)) * **tests:** fix lints ([#521](https://github.com/anthropics/anthropic-sdk-python/issues/521)) ([d96fc53](https://github.com/anthropics/anthropic-sdk-python/commit/d96fc530902bfe4b6a0c75044bf60e90f32997e4)) ### Documentation * **contributing:** update references to rye-up.com ([6486898](https://github.com/anthropics/anthropic-sdk-python/commit/6486898e874784f39be36a0a011867dd2fe8a5d5)) ## 0.26.1 (2024-05-21) Full Changelog: [v0.26.0...v0.26.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.26.0...v0.26.1) ### Chores * **docs:** fix typo ([#511](https://github.com/anthropics/anthropic-sdk-python/issues/511)) ([d7401bd](https://github.com/anthropics/anthropic-sdk-python/commit/d7401bdca637958171bad6b17406e8201c5bc6f6)) * **tools:** rely on pydantic's JSON parser instead of pydantic ([#510](https://github.com/anthropics/anthropic-sdk-python/issues/510)) ([8e7edca](https://github.com/anthropics/anthropic-sdk-python/commit/8e7edca45525be97a4a12a365db72b1668b3e4a1)) ## 0.26.0 (2024-05-16) Full Changelog: [v0.25.9...v0.26.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.25.9...v0.26.0) ### Features * **api:** add `tool_choice` param, image block params inside `tool_result.content`, and streaming for `tool_use` blocks ([#502](https://github.com/anthropics/anthropic-sdk-python/issues/502)) ([e0bc274](https://github.com/anthropics/anthropic-sdk-python/commit/e0bc2749d4be57fe9f0d60635b3198de89608bb9)) ### Chores * **internal:** minor formatting changes ([#500](https://github.com/anthropics/anthropic-sdk-python/issues/500)) ([8b32558](https://github.com/anthropics/anthropic-sdk-python/commit/8b32558e95d83badea1bfe4084fb5db86f7f78cd)) ## 0.25.9 (2024-05-14) Full Changelog: [v0.25.8...v0.25.9](https://github.com/anthropics/anthropic-sdk-python/compare/v0.25.8...v0.25.9) ### Bug Fixes * **types:** correct type for InputSchema ([#498](https://github.com/anthropics/anthropic-sdk-python/issues/498)) ([b86936c](https://github.com/anthropics/anthropic-sdk-python/commit/b86936ccb4ebe27bfb04a8fda2cbfdf88bbdc111)) ### Chores * **docs:** add SECURITY.md ([#493](https://github.com/anthropics/anthropic-sdk-python/issues/493)) ([d5cba46](https://github.com/anthropics/anthropic-sdk-python/commit/d5cba4634213b57f39dbc0f339c3320c651cf1bc)) * **internal:** add slightly better logging to scripts ([#497](https://github.com/anthropics/anthropic-sdk-python/issues/497)) ([acb0149](https://github.com/anthropics/anthropic-sdk-python/commit/acb0149b4659c932ca6f3abac46c4de166b5341b)) * **internal:** bump pydantic dependency ([#495](https://github.com/anthropics/anthropic-sdk-python/issues/495)) ([00cd840](https://github.com/anthropics/anthropic-sdk-python/commit/00cd8408254622d7e95812c0208fe09396d07ca4)) * **types:** add union discriminator metadata ([#491](https://github.com/anthropics/anthropic-sdk-python/issues/491)) ([95544a9](https://github.com/anthropics/anthropic-sdk-python/commit/95544a9e9fec7cfaab034355426a2f4634b8e26a)) ## 0.25.8 (2024-05-07) Full Changelog: [v0.25.7...v0.25.8](https://github.com/anthropics/anthropic-sdk-python/compare/v0.25.7...v0.25.8) ### Chores * **client:** log response headers in debug mode ([#480](https://github.com/anthropics/anthropic-sdk-python/issues/480)) ([d1c4d14](https://github.com/anthropics/anthropic-sdk-python/commit/d1c4d14c881b0e754ca220cdcda4d06fe23c81ab)) * **internal:** add link to openapi spec ([#484](https://github.com/anthropics/anthropic-sdk-python/issues/484)) ([876cd0d](https://github.com/anthropics/anthropic-sdk-python/commit/876cd0d5b30ca823c4088124ec303e0765d993b8)) * **internal:** add scripts/test, scripts/mock and add ci job ([#486](https://github.com/anthropics/anthropic-sdk-python/issues/486)) ([6111fe8](https://github.com/anthropics/anthropic-sdk-python/commit/6111fe897d8111f8b3e301923a94eabe1cb96558)) * **internal:** bump prism version ([#487](https://github.com/anthropics/anthropic-sdk-python/issues/487)) ([98fb3e6](https://github.com/anthropics/anthropic-sdk-python/commit/98fb3e63f16adccb6ff46d4c259d1953c91f041e)) ### Documentation * **readme:** fix misleading timeout example value ([#489](https://github.com/anthropics/anthropic-sdk-python/issues/489)) ([b465bce](https://github.com/anthropics/anthropic-sdk-python/commit/b465bce54de95d30190154dbfc53446b1586dade)) ## 0.25.7 (2024-04-29) Full Changelog: [v0.25.6...v0.25.7](https://github.com/anthropics/anthropic-sdk-python/compare/v0.25.6...v0.25.7) ### Bug Fixes * **docs:** doc improvements ([#472](https://github.com/anthropics/anthropic-sdk-python/issues/472)) ([1b6d4e2](https://github.com/anthropics/anthropic-sdk-python/commit/1b6d4e2c6be01dd794824a912cd78545d5bba135)) ### Chores * **internal:** minor reformatting ([#478](https://github.com/anthropics/anthropic-sdk-python/issues/478)) ([de4b2e0](https://github.com/anthropics/anthropic-sdk-python/commit/de4b2e088a997760e177abc765172bb495ccb978)) * **internal:** reformat imports ([#477](https://github.com/anthropics/anthropic-sdk-python/issues/477)) ([553e955](https://github.com/anthropics/anthropic-sdk-python/commit/553e955de5d6aae29ee28e1edfcc24d1ee9f3c25)) * **internal:** restructure imports ([#470](https://github.com/anthropics/anthropic-sdk-python/issues/470)) ([49e0044](https://github.com/anthropics/anthropic-sdk-python/commit/49e0044bcf1949699275d67dbed8dbf1c5412366)) * **internal:** update test helper function ([#476](https://github.com/anthropics/anthropic-sdk-python/issues/476)) ([f46e454](https://github.com/anthropics/anthropic-sdk-python/commit/f46e454f04ccb320fed2639235f9b382f3de27cd)) * **internal:** use actions/checkout@v4 for codeflow ([#474](https://github.com/anthropics/anthropic-sdk-python/issues/474)) ([8b18b52](https://github.com/anthropics/anthropic-sdk-python/commit/8b18b5211a200a1e09647441cd16244dfda05253)) * **tests:** rename test file ([#473](https://github.com/anthropics/anthropic-sdk-python/issues/473)) ([5b8261c](https://github.com/anthropics/anthropic-sdk-python/commit/5b8261c251e765ac239f1c0176ec3001b12769dd)) ## 0.25.6 (2024-04-18) Full Changelog: [v0.25.5...v0.25.6](https://github.com/anthropics/anthropic-sdk-python/compare/v0.25.5...v0.25.6) ### Chores * **internal:** bump pyright to 1.1.359 ([#466](https://github.com/anthropics/anthropic-sdk-python/issues/466)) ([8088160](https://github.com/anthropics/anthropic-sdk-python/commit/808816044cb33499c45e12b609f7a7664c628c88)) ## 0.25.5 (2024-04-17) Full Changelog: [v0.25.4...v0.25.5](https://github.com/anthropics/anthropic-sdk-python/compare/v0.25.4...v0.25.5) ### Chores * **internal:** ban usage of lru_cache ([#464](https://github.com/anthropics/anthropic-sdk-python/issues/464)) ([dc8ca22](https://github.com/anthropics/anthropic-sdk-python/commit/dc8ca22b1994af994ce9502494f4df1741c0559d)) ## 0.25.4 (2024-04-17) Full Changelog: [v0.25.3...v0.25.4](https://github.com/anthropics/anthropic-sdk-python/compare/v0.25.3...v0.25.4) ### Bug Fixes * **bedrock:** correct auth implementation ([#462](https://github.com/anthropics/anthropic-sdk-python/issues/462)) ([2f456f5](https://github.com/anthropics/anthropic-sdk-python/commit/2f456f59f42876dfabde94b6e36f9349fc409aef)) ## 0.25.3 (2024-04-17) Full Changelog: [v0.25.2...v0.25.3](https://github.com/anthropics/anthropic-sdk-python/compare/v0.25.2...v0.25.3) ### Chores * **bedrock:** cache boto sessions ([#455](https://github.com/anthropics/anthropic-sdk-python/issues/455)) ([d58adef](https://github.com/anthropics/anthropic-sdk-python/commit/d58adefc7097d98e25bb1665be2037f968000d76)) ## 0.25.2 (2024-04-15) Full Changelog: [v0.25.1...v0.25.2](https://github.com/anthropics/anthropic-sdk-python/compare/v0.25.1...v0.25.2) ### Chores * **internal:** formatting ([#452](https://github.com/anthropics/anthropic-sdk-python/issues/452)) ([8ac016b](https://github.com/anthropics/anthropic-sdk-python/commit/8ac016b3be19247a7323f3f9fb5aad4d4f30ced5)) ## 0.25.1 (2024-04-11) Full Changelog: [v0.25.0...v0.25.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.25.0...v0.25.1) ### Chores * fix typo ([#449](https://github.com/anthropics/anthropic-sdk-python/issues/449)) ([420a6c5](https://github.com/anthropics/anthropic-sdk-python/commit/420a6c5081ecd58e16b40ca5dfca582aa704c34a)) ## 0.25.0 (2024-04-09) Full Changelog: [v0.24.0...v0.25.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.24.0...v0.25.0) ### Features * **bedrock:** add `copy` / `with_options` to bedrock client ([8af7c41](https://github.com/anthropics/anthropic-sdk-python/commit/8af7c41886c9e599a2199e3e496d9d04157699da)) ### Chores * unknown commit message ([8af7c41](https://github.com/anthropics/anthropic-sdk-python/commit/8af7c41886c9e599a2199e3e496d9d04157699da)) ## 0.24.0 (2024-04-09) Full Changelog: [v0.23.1...v0.24.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.23.1...v0.24.0) ### Features * **client:** add DefaultHttpxClient and DefaultAsyncHttpxClient ([#444](https://github.com/anthropics/anthropic-sdk-python/issues/444)) ([51d2427](https://github.com/anthropics/anthropic-sdk-python/commit/51d2427c0bb51cbd17d55f827da7fb9cc05f5d06)) * **models:** add to_dict & to_json helper methods ([#446](https://github.com/anthropics/anthropic-sdk-python/issues/446)) ([6709f58](https://github.com/anthropics/anthropic-sdk-python/commit/6709f58d0980669100ea0b7935259d3c05cf9648)) ## 0.23.1 (2024-04-04) Full Changelog: [v0.23.0...v0.23.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.23.0...v0.23.1) ### Documentation * **readme:** mention tool use ([#441](https://github.com/anthropics/anthropic-sdk-python/issues/441)) ([e6cd916](https://github.com/anthropics/anthropic-sdk-python/commit/e6cd916b5f4d9cbbae4610828ffb51d81404d74f)) ## 0.23.0 (2024-04-04) Full Changelog: [v0.22.1...v0.23.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.22.1...v0.23.0) ### Features * **api:** tool use beta ([#438](https://github.com/anthropics/anthropic-sdk-python/issues/438)) ([5e35ffe](https://github.com/anthropics/anthropic-sdk-python/commit/5e35ffeec0a38055bba2f3998aa3e7c85790627a)) ## 0.22.1 (2024-04-04) Full Changelog: [v0.22.0...v0.22.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.22.0...v0.22.1) ### Bug Fixes * **types:** correctly mark type as a required property in requests ([#435](https://github.com/anthropics/anthropic-sdk-python/issues/435)) ([efc35ec](https://github.com/anthropics/anthropic-sdk-python/commit/efc35ec7b87b4e7033509431e828fdf42579f74d)) ### Chores * **types:** consistent naming for text block types ([#437](https://github.com/anthropics/anthropic-sdk-python/issues/437)) ([e979fe1](https://github.com/anthropics/anthropic-sdk-python/commit/e979fe14f868e1bc428440c00092decc590bb545)) ## 0.22.0 (2024-04-04) Full Changelog: [v0.21.3...v0.22.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.21.3...v0.22.0) ### Features * **client:** increase default HTTP max_connections to 1000 and max_keepalive_connections to 100 ([#428](https://github.com/anthropics/anthropic-sdk-python/issues/428)) ([9a43940](https://github.com/anthropics/anthropic-sdk-python/commit/9a4394008db937a9ad851589b9adfbd9e15333ef)) * **package:** export default constants ([#423](https://github.com/anthropics/anthropic-sdk-python/issues/423)) ([0d694e1](https://github.com/anthropics/anthropic-sdk-python/commit/0d694e157b040993d937f136c5072c98b87434ff)) ### Bug Fixes * **client:** correct logic for line decoding in streaming ([#433](https://github.com/anthropics/anthropic-sdk-python/issues/433)) ([6bf9379](https://github.com/anthropics/anthropic-sdk-python/commit/6bf93794127a62a077f2e50a2acfe01464742319)) * **project:** use absolute github links on PyPi ([#427](https://github.com/anthropics/anthropic-sdk-python/issues/427)) ([cbd8b1c](https://github.com/anthropics/anthropic-sdk-python/commit/cbd8b1c789e83d2c84ba10165778e4ad2af1ac20)) * revert regression with 3.7 support ([#419](https://github.com/anthropics/anthropic-sdk-python/issues/419)) ([fa21f36](https://github.com/anthropics/anthropic-sdk-python/commit/fa21f3643714d985b10b45dc8bfc3887ed20eba7)) * **streaming:** correct accumulation of output tokens ([#426](https://github.com/anthropics/anthropic-sdk-python/issues/426)) ([b50ed05](https://github.com/anthropics/anthropic-sdk-python/commit/b50ed05a991f02bccfd9f65a1c59e56540adba08)) ### Chores * **client:** validate that max_retries is not None ([#430](https://github.com/anthropics/anthropic-sdk-python/issues/430)) ([31b2a2f](https://github.com/anthropics/anthropic-sdk-python/commit/31b2a2fd4069a670c795eeaf51b641fbf2097af1)) * **internal:** bump dependencies ([#421](https://github.com/anthropics/anthropic-sdk-python/issues/421)) ([30e8031](https://github.com/anthropics/anthropic-sdk-python/commit/30e8031469a4c4beb0bb906920f53d5d4da2e2c3)) * **internal:** defer model build for import latency ([#431](https://github.com/anthropics/anthropic-sdk-python/issues/431)) ([51d4783](https://github.com/anthropics/anthropic-sdk-python/commit/51d47832ae44415604725bb763cf567fb9dc1b34)) * **internal:** formatting change ([#415](https://github.com/anthropics/anthropic-sdk-python/issues/415)) ([1474f44](https://github.com/anthropics/anthropic-sdk-python/commit/1474f443201949c9b8a7d0a8562968d57d421fb5)) ### Documentation * **contributing:** fix typo ([#414](https://github.com/anthropics/anthropic-sdk-python/issues/414)) ([aeaf995](https://github.com/anthropics/anthropic-sdk-python/commit/aeaf99573a9140b6bb5c0af4cefddbd6f469a6a5)) * **readme:** change undocumented params wording ([#429](https://github.com/anthropics/anthropic-sdk-python/issues/429)) ([1336958](https://github.com/anthropics/anthropic-sdk-python/commit/13369583fc74101e002427079c9871e05e5536e8)) ## 0.21.3 (2024-03-21) Full Changelog: [v0.21.2...v0.21.3](https://github.com/anthropics/anthropic-sdk-python/compare/v0.21.2...v0.21.3) ### Bug Fixes * **types:** correct typo claude-2.1' to claude-2.1 ([#400](https://github.com/anthropics/anthropic-sdk-python/issues/400)) ([7f82aa3](https://github.com/anthropics/anthropic-sdk-python/commit/7f82aa3aa28c7134b69eeb42d5f0b7523c7cb5df)) * **types:** correct typo claude-2.1' to claude-2.1 ([#413](https://github.com/anthropics/anthropic-sdk-python/issues/413)) ([bb1aebe](https://github.com/anthropics/anthropic-sdk-python/commit/bb1aebe6225b7d854b8125344846e77c6e13f3f9)) ## 0.21.2 (2024-03-21) Full Changelog: [v0.21.1...v0.21.2](https://github.com/anthropics/anthropic-sdk-python/compare/v0.21.1...v0.21.2) ### Documentation * **readme:** consistent use of sentence case in headings ([#405](https://github.com/anthropics/anthropic-sdk-python/issues/405)) ([495ca87](https://github.com/anthropics/anthropic-sdk-python/commit/495ca87e95ac645d4f387614adac1a20c26729b9)) * **readme:** document how to make undocumented requests ([#407](https://github.com/anthropics/anthropic-sdk-python/issues/407)) ([b046d0d](https://github.com/anthropics/anthropic-sdk-python/commit/b046d0dd5be5fc9a21f9ac352627b7bb5e2b9ced)) ## 0.21.1 (2024-03-20) Full Changelog: [v0.21.0...v0.21.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.21.0...v0.21.1) ### Chores * **internal:** loosen input type for util function ([#402](https://github.com/anthropics/anthropic-sdk-python/issues/402)) ([9a6ca55](https://github.com/anthropics/anthropic-sdk-python/commit/9a6ca5528ee5b96577df4d657937c35cdc263f85)) ## 0.21.0 (2024-03-19) Full Changelog: [v0.20.0...v0.21.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.20.0...v0.21.0) ### Features * **vertex:** api is no longer in private beta ([#399](https://github.com/anthropics/anthropic-sdk-python/issues/399)) ([4cb0e64](https://github.com/anthropics/anthropic-sdk-python/commit/4cb0e6453ed185646b652b7942ed75f8e49be8e3)) ### Performance Improvements * cache TypeAdapters ([#396](https://github.com/anthropics/anthropic-sdk-python/issues/396)) ([a902c47](https://github.com/anthropics/anthropic-sdk-python/commit/a902c472b986d7c7bfda52fc20d737f0bcf80b6a)) ### Chores * **internal:** update generated pragma comment ([#398](https://github.com/anthropics/anthropic-sdk-python/issues/398)) ([330b61e](https://github.com/anthropics/anthropic-sdk-python/commit/330b61eccfd8af3ee587a91dd2491d66abfe159a)) ### Documentation * fix typo in CONTRIBUTING.md ([#397](https://github.com/anthropics/anthropic-sdk-python/issues/397)) ([d46629f](https://github.com/anthropics/anthropic-sdk-python/commit/d46629f385b65a0c099ca7a94ebaae9bcb0ecb2c)) * **helpers:** fix example code ([#391](https://github.com/anthropics/anthropic-sdk-python/issues/391)) ([9fe0c8b](https://github.com/anthropics/anthropic-sdk-python/commit/9fe0c8b9b257d18e7f5fb7ac03de2073552c083d)) ## 0.20.0 (2024-03-13) Full Changelog: [v0.19.2...v0.20.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.19.2...v0.20.0) ### Features * **api:** add haiku model ([#390](https://github.com/anthropics/anthropic-sdk-python/issues/390)) ([43b57fc](https://github.com/anthropics/anthropic-sdk-python/commit/43b57fca5426774929bfcac81bf00659740db796)) ### Documentation * **readme:** mention vertex API ([#388](https://github.com/anthropics/anthropic-sdk-python/issues/388)) ([8bb6b98](https://github.com/anthropics/anthropic-sdk-python/commit/8bb6b9841db322db8c5e8357c2c379482be15441)) ## 0.19.2 (2024-03-11) Full Changelog: [v0.19.1...v0.19.2](https://github.com/anthropics/anthropic-sdk-python/compare/v0.19.1...v0.19.2) ### Bug Fixes * **vertex:** use correct auth scopes ([#385](https://github.com/anthropics/anthropic-sdk-python/issues/385)) ([e4de056](https://github.com/anthropics/anthropic-sdk-python/commit/e4de056ddc24e2d3d8f742124b0a965ff3404341)) ### Chores * export NOT_GIVEN sentinel value ([#379](https://github.com/anthropics/anthropic-sdk-python/issues/379)) ([ba127bc](https://github.com/anthropics/anthropic-sdk-python/commit/ba127bc44b70490a7c9e8ff76b7a742631e94c5c)) * **internal:** improve deserialisation of discriminated unions ([#386](https://github.com/anthropics/anthropic-sdk-python/issues/386)) ([fbc7e0b](https://github.com/anthropics/anthropic-sdk-python/commit/fbc7e0b2cf5e8f5bcd316393a7483509ed9f790e)) * **internal:** support parsing Annotated types ([#377](https://github.com/anthropics/anthropic-sdk-python/issues/377)) ([f44efd5](https://github.com/anthropics/anthropic-sdk-python/commit/f44efd5a587fca5021bfc2c068a715a7a550a5d0)) ## 0.19.1 (2024-03-06) Full Changelog: [v0.19.0...v0.19.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.19.0...v0.19.1) ### Chores * **internal:** add core support for deserializing into number response ([#373](https://github.com/anthropics/anthropic-sdk-python/issues/373)) ([b62c422](https://github.com/anthropics/anthropic-sdk-python/commit/b62c4224fafe0544877ebb57278526a5ddd1955d)) ## 0.19.0 (2024-03-06) Full Changelog: [v0.18.1...v0.19.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.18.1...v0.19.0) ### Features * **api:** add enum to model param for message ([#371](https://github.com/anthropics/anthropic-sdk-python/issues/371)) ([f96765f](https://github.com/anthropics/anthropic-sdk-python/commit/f96765f188676bb688f599a3574c16dbfb27430c)) ### Chores * **client:** improve error message for invalid http_client argument ([#367](https://github.com/anthropics/anthropic-sdk-python/issues/367)) ([2f4df72](https://github.com/anthropics/anthropic-sdk-python/commit/2f4df724410bc6213bf559739724bec0242becd7)) ### Documentation * **readme:** fix async streaming snippet ([#366](https://github.com/anthropics/anthropic-sdk-python/issues/366)) ([37c469d](https://github.com/anthropics/anthropic-sdk-python/commit/37c469deecad9f6244d42dce7d3cedc289ca129b)) ## 0.18.1 (2024-03-04) Full Changelog: [v0.18.0...v0.18.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.18.0...v0.18.1) ### Chores * **readme:** update bedrock example ([#364](https://github.com/anthropics/anthropic-sdk-python/issues/364)) ([81e4d10](https://github.com/anthropics/anthropic-sdk-python/commit/81e4d10f6b7c5e06d5d2844441350731dbddbfad)) ## 0.18.0 (2024-03-04) Full Changelog: [v0.17.0...v0.18.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.17.0...v0.18.0) ### Features * **bedrock:** add messages API ([#362](https://github.com/anthropics/anthropic-sdk-python/issues/362)) ([5409be9](https://github.com/anthropics/anthropic-sdk-python/commit/5409be98d0fd4e65e6dd766238fc8789efb3cb49)) ### Chores * remove old examples ([4895381](https://github.com/anthropics/anthropic-sdk-python/commit/489538163ada7de07c3f4b5237c551949fee4232)) ## 0.17.0 (2024-03-04) Full Changelog: [v0.16.0...v0.17.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.16.0...v0.17.0) ### Features * **messages:** add support for image inputs ([#359](https://github.com/anthropics/anthropic-sdk-python/issues/359)) ([579f013](https://github.com/anthropics/anthropic-sdk-python/commit/579f013dd294f34b3c44e2b331a4aa25b6cdfd6a)) ### Chores * **client:** use anyio.sleep instead of asyncio.sleep ([#351](https://github.com/anthropics/anthropic-sdk-python/issues/351)) ([2778a22](https://github.com/anthropics/anthropic-sdk-python/commit/2778a2228e82704dde9176d970274e806422c02b)) * **docs:** mention install from git repo ([#356](https://github.com/anthropics/anthropic-sdk-python/issues/356)) ([9d503ba](https://github.com/anthropics/anthropic-sdk-python/commit/9d503ba9cc462e33166594ca19f666819a3a5a87)) * **docs:** remove references to old bedrock package ([#344](https://github.com/anthropics/anthropic-sdk-python/issues/344)) ([3323883](https://github.com/anthropics/anthropic-sdk-python/commit/3323883750b9d61fa084cadc99519b2f6cf8d39c)) * **internal:** bump pyright ([#350](https://github.com/anthropics/anthropic-sdk-python/issues/350)) ([ee0161c](https://github.com/anthropics/anthropic-sdk-python/commit/ee0161c2d7d2fefd06ee5b1001131cd6d6d236d7)) * **internal:** bump rye to v0.24.0 ([#348](https://github.com/anthropics/anthropic-sdk-python/issues/348)) ([be8597b](https://github.com/anthropics/anthropic-sdk-python/commit/be8597b33c2f2f6e8b9ae77738f4c898e48c8e91)) * **internal:** improve bedrock streaming setup ([#354](https://github.com/anthropics/anthropic-sdk-python/issues/354)) ([2b55c68](https://github.com/anthropics/anthropic-sdk-python/commit/2b55c688514e4b13abce547362f0c0a3e7f0e97f)) * **internal:** refactor release environment script ([#347](https://github.com/anthropics/anthropic-sdk-python/issues/347)) ([a87443a](https://github.com/anthropics/anthropic-sdk-python/commit/a87443a90374aedaac80451f61046c6f1aefeaa9)) * **internal:** split up transforms into sync / async ([#357](https://github.com/anthropics/anthropic-sdk-python/issues/357)) ([f55ee71](https://github.com/anthropics/anthropic-sdk-python/commit/f55ee71b0b517f3e605bfd7a4aa948a9c2fc6552)) * **internal:** support more input types ([#358](https://github.com/anthropics/anthropic-sdk-python/issues/358)) ([35b0347](https://github.com/anthropics/anthropic-sdk-python/commit/35b0347bfddecc94fc8ac09b42ff3d96f4523bf8)) * **internal:** update deps ([#349](https://github.com/anthropics/anthropic-sdk-python/issues/349)) ([ab82b2d](https://github.com/anthropics/anthropic-sdk-python/commit/ab82b2d7ce16f3fed4b20e60f0c8e7981c22c191)) ### Documentation * **contributing:** improve wording ([#355](https://github.com/anthropics/anthropic-sdk-python/issues/355)) ([f9093a0](https://github.com/anthropics/anthropic-sdk-python/commit/f9093a0ee8d590185f572749d58280f7eda5ed8b)) ### Refactors * **api:** mark completions API as legacy ([#346](https://github.com/anthropics/anthropic-sdk-python/issues/346)) ([2bb25a1](https://github.com/anthropics/anthropic-sdk-python/commit/2bb25a12509b87557f3da2125ab955b60e32713f)) ## 0.16.0 (2024-02-13) Full Changelog: [v0.15.1...v0.16.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.15.1...v0.16.0) ### Features * **api:** messages is generally available ([#343](https://github.com/anthropics/anthropic-sdk-python/issues/343)) ([f682594](https://github.com/anthropics/anthropic-sdk-python/commit/f6825941acc09b33af386b40718bd2f3c01b29ef)) * **messages:** allow message response in params ([#339](https://github.com/anthropics/anthropic-sdk-python/issues/339)) ([86c63f0](https://github.com/anthropics/anthropic-sdk-python/commit/86c63f0e7441a9fe894b3ae7cd7e871060d5ebbf)) ### Documentation * add CONTRIBUTING.md ([#340](https://github.com/anthropics/anthropic-sdk-python/issues/340)) ([78469ad](https://github.com/anthropics/anthropic-sdk-python/commit/78469ade1658bf6b12b7cb947136e228d6992303)) ## 0.15.1 (2024-02-07) Full Changelog: [v0.15.0...v0.15.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.15.0...v0.15.1) ### Bug Fixes * prevent crash when platform.architecture() is not allowed ([#334](https://github.com/anthropics/anthropic-sdk-python/issues/334)) ([fefb5c1](https://github.com/anthropics/anthropic-sdk-python/commit/fefb5c10c10054f28fcccf0d9f44204de93e9fe3)) * **types:** loosen most List params types to Iterable ([#338](https://github.com/anthropics/anthropic-sdk-python/issues/338)) ([6e7761b](https://github.com/anthropics/anthropic-sdk-python/commit/6e7761b89c9ef226bd8f7df465445526c08fdb2f)) ### Chores * **internal:** add lint command ([#337](https://github.com/anthropics/anthropic-sdk-python/issues/337)) ([2ebaf1d](https://github.com/anthropics/anthropic-sdk-python/commit/2ebaf1d6a85b638b502661735e3ffc5b58d5c241)) * **internal:** support serialising iterable types ([#336](https://github.com/anthropics/anthropic-sdk-python/issues/336)) ([ea3ed7b](https://github.com/anthropics/anthropic-sdk-python/commit/ea3ed7b8b91314721129480d164d7bf3bafec26c)) ## 0.15.0 (2024-02-02) Full Changelog: [v0.14.1...v0.15.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.14.1...v0.15.0) ### Features * **api:** add new usage response fields ([#332](https://github.com/anthropics/anthropic-sdk-python/issues/332)) ([554098e](https://github.com/anthropics/anthropic-sdk-python/commit/554098e544d49575d2d9d24edfb46f2fa0f77ba1)) ## 0.14.1 (2024-02-02) Full Changelog: [v0.14.0...v0.14.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.14.0...v0.14.1) ### Chores * **interal:** make link to api.md relative ([#330](https://github.com/anthropics/anthropic-sdk-python/issues/330)) ([e393317](https://github.com/anthropics/anthropic-sdk-python/commit/e393317362d8cd74442d7a802ea965211c913115)) ## 0.14.0 (2024-01-31) Full Changelog: [v0.13.0...v0.14.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.13.0...v0.14.0) ### Features * **bedrock:** include bedrock SDK ([#328](https://github.com/anthropics/anthropic-sdk-python/issues/328)) ([a03f21f](https://github.com/anthropics/anthropic-sdk-python/commit/a03f21fef1ab3225f9839002b69aa5cb5840b375)) ## 0.13.0 (2024-01-30) Full Changelog: [v0.12.0...v0.13.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.12.0...v0.13.0) ### Features * **client:** support parsing custom response types ([#325](https://github.com/anthropics/anthropic-sdk-python/issues/325)) ([416633f](https://github.com/anthropics/anthropic-sdk-python/commit/416633fedb962d207fb841e80d7d7947fe52bb33)) ### Chores * **internal:** cast type in mocked test ([#326](https://github.com/anthropics/anthropic-sdk-python/issues/326)) ([fd22d8e](https://github.com/anthropics/anthropic-sdk-python/commit/fd22d8e584c5f3d6a029b4b0e87b98827746fda9)) * **internal:** enable ruff type checking misuse lint rule ([#324](https://github.com/anthropics/anthropic-sdk-python/issues/324)) ([6587598](https://github.com/anthropics/anthropic-sdk-python/commit/6587598162387c0aada958df22610a93198e813d)) * **internal:** support multipart data with overlapping keys ([#322](https://github.com/anthropics/anthropic-sdk-python/issues/322)) ([9ecab60](https://github.com/anthropics/anthropic-sdk-python/commit/9ecab6048afeca544146b9629bcdaa5250012cc9)) * **internal:** support pre-release versioning ([#327](https://github.com/anthropics/anthropic-sdk-python/issues/327)) ([78b1bfe](https://github.com/anthropics/anthropic-sdk-python/commit/78b1bfe3e694e0400477fc25ae1aaab34c28e61e)) ## 0.12.0 (2024-01-25) Full Changelog: [v0.11.0...v0.12.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.11.0...v0.12.0) ### Features * **client:** enable follow redirects by default ([#320](https://github.com/anthropics/anthropic-sdk-python/issues/320)) ([9959c32](https://github.com/anthropics/anthropic-sdk-python/commit/9959c32d24acd7199e6ce8124a18bcfa263fac85)) ## 0.11.0 (2024-01-23) Full Changelog: [v0.10.0...v0.11.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.10.0...v0.11.0) ### Features * **vertex:** add support for google vertex ([#319](https://github.com/anthropics/anthropic-sdk-python/issues/319)) ([5324415](https://github.com/anthropics/anthropic-sdk-python/commit/53244155d657e782d4ec9cc85f557233ee3698be)) ### Chores * **internal:** add internal helpers ([#316](https://github.com/anthropics/anthropic-sdk-python/issues/316)) ([8c75cdf](https://github.com/anthropics/anthropic-sdk-python/commit/8c75cdfe5e236c08bb6ecc09e27f69932cc523f1)) * **internal:** update resource client type ([#318](https://github.com/anthropics/anthropic-sdk-python/issues/318)) ([bdd8d84](https://github.com/anthropics/anthropic-sdk-python/commit/bdd8d84023814f390b8f5eca7bd64cb340c1e8a8)) ## 0.10.0 (2024-01-18) Full Changelog: [v0.9.0...v0.10.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.9.0...v0.10.0) ### Features * **client:** add support for streaming raw responses ([#307](https://github.com/anthropics/anthropic-sdk-python/issues/307)) ([f295982](https://github.com/anthropics/anthropic-sdk-python/commit/f2959827fe2cd555db38a62c1b3df1a12e6dee40)) ### Bug Fixes * **ci:** ignore stainless-app edits to release PR title ([#315](https://github.com/anthropics/anthropic-sdk-python/issues/315)) ([69e8b03](https://github.com/anthropics/anthropic-sdk-python/commit/69e8b03cd12e3c12de7c528a0b2c064f709a239a)) ### Chores * add write_to_file binary helper method ([#309](https://github.com/anthropics/anthropic-sdk-python/issues/309)) ([8ac7988](https://github.com/anthropics/anthropic-sdk-python/commit/8ac7988dee11745495290f38fa5a2b8fddd0b993)) * **client:** improve debug logging for failed requests ([#303](https://github.com/anthropics/anthropic-sdk-python/issues/303)) ([5e58c25](https://github.com/anthropics/anthropic-sdk-python/commit/5e58c2537eccadbccef9aadcd6433cf35328e678)) * **internal:** fix typing util function ([#310](https://github.com/anthropics/anthropic-sdk-python/issues/310)) ([3671aa6](https://github.com/anthropics/anthropic-sdk-python/commit/3671aa6b3b05776b727a727020366bb6c349f66a)) * **internal:** remove redundant client test ([#311](https://github.com/anthropics/anthropic-sdk-python/issues/311)) ([d7140f7](https://github.com/anthropics/anthropic-sdk-python/commit/d7140f7c16554dfacdac642173516625f2540496)) * **internal:** share client instances between all tests ([#314](https://github.com/anthropics/anthropic-sdk-python/issues/314)) ([ccf731b](https://github.com/anthropics/anthropic-sdk-python/commit/ccf731b047809264d073f86c08c7f36ee360fda1)) * **internal:** speculative retry-after-ms support ([#312](https://github.com/anthropics/anthropic-sdk-python/issues/312)) ([4b27da9](https://github.com/anthropics/anthropic-sdk-python/commit/4b27da9d05ce90944f566c20b122653adc0b9ab1)) * **internal:** updates to proxy helper ([#308](https://github.com/anthropics/anthropic-sdk-python/issues/308)) ([a0b3cdb](https://github.com/anthropics/anthropic-sdk-python/commit/a0b3cdb655d150d3703f793c82e4a3945f45c82f)) * lazy load raw resource class properties ([#313](https://github.com/anthropics/anthropic-sdk-python/issues/313)) ([b13f824](https://github.com/anthropics/anthropic-sdk-python/commit/b13f8249be1a4f77611598b4cce465481af35d83)) ### Documentation * **readme:** improve api reference ([#306](https://github.com/anthropics/anthropic-sdk-python/issues/306)) ([c3ab836](https://github.com/anthropics/anthropic-sdk-python/commit/c3ab836e4654dff259f19071bf0e1cdff249a268)) ## 0.9.0 (2024-01-08) Full Changelog: [v0.8.1...v0.9.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.8.1...v0.9.0) ### Features * add `None` default value to nullable response properties ([#299](https://github.com/anthropics/anthropic-sdk-python/issues/299)) ([da423db](https://github.com/anthropics/anthropic-sdk-python/commit/da423db5c14b213c52fe0986981c4f01aff0d2c3)) ### Bug Fixes * **client:** correctly use custom http client auth ([#296](https://github.com/anthropics/anthropic-sdk-python/issues/296)) ([6289d6e](https://github.com/anthropics/anthropic-sdk-python/commit/6289d6e205f872c02114f05333d5426055f2416f)) ### Chores * add .keep files for examples and custom code directories ([#302](https://github.com/anthropics/anthropic-sdk-python/issues/302)) ([73a07ea](https://github.com/anthropics/anthropic-sdk-python/commit/73a07ea7a5254d205b68e25c46c1f2267604ac9b)) * **internal:** loosen type var restrictions ([#301](https://github.com/anthropics/anthropic-sdk-python/issues/301)) ([5e5e1e7](https://github.com/anthropics/anthropic-sdk-python/commit/5e5e1e716a8732af66e2234307521b4620b07361)) * **internal:** replace isort with ruff ([#298](https://github.com/anthropics/anthropic-sdk-python/issues/298)) ([7c60904](https://github.com/anthropics/anthropic-sdk-python/commit/7c60904f5da10c4ef6ab8af4e8631bc938b35131)) * use property declarations for resource members ([#300](https://github.com/anthropics/anthropic-sdk-python/issues/300)) ([8671297](https://github.com/anthropics/anthropic-sdk-python/commit/8671297b87105635accefd574c44dbffd8a4f9e9)) ## 0.8.1 (2023-12-22) Full Changelog: [v0.8.0...v0.8.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.8.0...v0.8.1) ### Chores * **internal:** add bin script ([#292](https://github.com/anthropics/anthropic-sdk-python/issues/292)) ([ba2953d](https://github.com/anthropics/anthropic-sdk-python/commit/ba2953dcaa8a8fcebaa7e8891304687c95b17499)) * **internal:** fix typos ([#287](https://github.com/anthropics/anthropic-sdk-python/issues/287)) ([4ffbcdf](https://github.com/anthropics/anthropic-sdk-python/commit/4ffbcdf1d3c8c2fbaf7152d207b24cdb0ea82ac9)) * **internal:** use ruff instead of black for formatting ([#294](https://github.com/anthropics/anthropic-sdk-python/issues/294)) ([1753887](https://github.com/anthropics/anthropic-sdk-python/commit/1753887a776f41bdc2d648329cfe6f20c91125e5)) * **package:** bump minimum typing-extensions to 4.7 ([#290](https://github.com/anthropics/anthropic-sdk-python/issues/290)) ([9ec5c57](https://github.com/anthropics/anthropic-sdk-python/commit/9ec5c57ba9a14a769d540e48755b05a1c190b45b)) ### Documentation * **messages:** improvements to helpers reference + typos ([#291](https://github.com/anthropics/anthropic-sdk-python/issues/291)) ([d18a895](https://github.com/anthropics/anthropic-sdk-python/commit/d18a895d380fc0c6610443486d73247b0cd97376)) * **readme:** remove old migration guide ([#289](https://github.com/anthropics/anthropic-sdk-python/issues/289)) ([eec4574](https://github.com/anthropics/anthropic-sdk-python/commit/eec4574f1f6668804c88bda67b901db10400fbc3)) ## 0.8.0 (2023-12-19) Full Changelog: [v0.7.8...v0.8.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.7.8...v0.8.0) ### Features * **api:** add messages endpoint with streaming helpers ([#286](https://github.com/anthropics/anthropic-sdk-python/issues/286)) ([c464b87](https://github.com/anthropics/anthropic-sdk-python/commit/c464b87b72ebbf9255418a02c627b0f0c52d03dd)) ### Chores * **ci:** run release workflow once per day ([#282](https://github.com/anthropics/anthropic-sdk-python/issues/282)) ([3a23912](https://github.com/anthropics/anthropic-sdk-python/commit/3a239127713c68ae53fa8b338e1f60ca25840a90)) * **client:** only import tokenizers when needed ([#284](https://github.com/anthropics/anthropic-sdk-python/issues/284)) ([b9e38b2](https://github.com/anthropics/anthropic-sdk-python/commit/b9e38b2a2e2be2b5fb31842fa409b95abcbccbc6)) * **streaming:** update constructor to use direct client names ([#285](https://github.com/anthropics/anthropic-sdk-python/issues/285)) ([0c55c84](https://github.com/anthropics/anthropic-sdk-python/commit/0c55c84ab3527199401f387fbc3338572b264fef)) ## 0.7.8 (2023-12-12) Full Changelog: [v0.7.7...v0.7.8](https://github.com/anthropics/anthropic-sdk-python/compare/v0.7.7...v0.7.8) ### Bug Fixes * avoid leaking memory when Client.with_options is used ([#275](https://github.com/anthropics/anthropic-sdk-python/issues/275)) ([5e51ebd](https://github.com/anthropics/anthropic-sdk-python/commit/5e51ebdbc6e5c23c8c237b5e0231ef66f585f964)) * **client:** correct base_url setter implementation ([#265](https://github.com/anthropics/anthropic-sdk-python/issues/265)) ([29d0c8b](https://github.com/anthropics/anthropic-sdk-python/commit/29d0c8b0eb174b499a904e02cce7fe7a6aaa1a01)) * **client:** ensure retried requests are closed ([#261](https://github.com/anthropics/anthropic-sdk-python/issues/261)) ([5d9aa75](https://github.com/anthropics/anthropic-sdk-python/commit/5d9aa754ace5d53eb90c1055dd6b1ca8e7deee4f)) * **errors:** properly assign APIError.body ([#274](https://github.com/anthropics/anthropic-sdk-python/issues/274)) ([342846f](https://github.com/anthropics/anthropic-sdk-python/commit/342846fa4d424a4d18dd2289d2b652bf53c97901)) ### Chores * **internal:** enable more lint rules ([#273](https://github.com/anthropics/anthropic-sdk-python/issues/273)) ([0ac62bc](https://github.com/anthropics/anthropic-sdk-python/commit/0ac62bc127ddf0367561427836ff19c1272fb0e1)) * **internal:** reformat imports ([#270](https://github.com/anthropics/anthropic-sdk-python/issues/270)) ([dc55724](https://github.com/anthropics/anthropic-sdk-python/commit/dc55724673dfa59911a05fe4827b8804beba0b05)) * **internal:** reformat imports ([#272](https://github.com/anthropics/anthropic-sdk-python/issues/272)) ([0d82ce4](https://github.com/anthropics/anthropic-sdk-python/commit/0d82ce4784c3a6c9599e6c09b8190e97ea028dc3)) * **internal:** remove unused file ([#264](https://github.com/anthropics/anthropic-sdk-python/issues/264)) ([1bfc69b](https://github.com/anthropics/anthropic-sdk-python/commit/1bfc69b0e2a1eb79598409cbfcba060f699d28a7)) * **internal:** replace string concatenation with f-strings ([#263](https://github.com/anthropics/anthropic-sdk-python/issues/263)) ([f545c35](https://github.com/anthropics/anthropic-sdk-python/commit/f545c350dd802079d057d34ff29444e32dc7bdcb)) * **internal:** update formatting ([#271](https://github.com/anthropics/anthropic-sdk-python/issues/271)) ([802ab59](https://github.com/anthropics/anthropic-sdk-python/commit/802ab59401b06986b8023e9ef0d0f9e0d6858b86)) * **package:** lift anyio v4 restriction ([#266](https://github.com/anthropics/anthropic-sdk-python/issues/266)) ([a217e99](https://github.com/anthropics/anthropic-sdk-python/commit/a217e9955569852d35ab1bc1351dd66ba807fc44)) ### Documentation * update examples to show claude-2.1 ([#276](https://github.com/anthropics/anthropic-sdk-python/issues/276)) ([8f562f4](https://github.com/anthropics/anthropic-sdk-python/commit/8f562f47f13ffaaab93f08b9b4c59d06e4a18b6c)) ### Refactors * **client:** simplify cleanup ([#278](https://github.com/anthropics/anthropic-sdk-python/issues/278)) ([3611ae2](https://github.com/anthropics/anthropic-sdk-python/commit/3611ae24d93fa33e55f2e9193a3c787bfd041da5)) * simplify internal error handling ([#279](https://github.com/anthropics/anthropic-sdk-python/issues/279)) ([993b51a](https://github.com/anthropics/anthropic-sdk-python/commit/993b51aa4f41bae3938a12d60919065c4865a734)) ## 0.7.7 (2023-11-29) Full Changelog: [v0.7.6...v0.7.7](https://github.com/anthropics/anthropic-sdk-python/compare/v0.7.6...v0.7.7) ### Chores * **internal:** add tests for proxy change ([#260](https://github.com/anthropics/anthropic-sdk-python/issues/260)) ([3b52136](https://github.com/anthropics/anthropic-sdk-python/commit/3b521362f6ee33c3ff66371e4f2d3bdcea2827bb)) * **internal:** updates to proxy helper ([#258](https://github.com/anthropics/anthropic-sdk-python/issues/258)) ([94c4de8](https://github.com/anthropics/anthropic-sdk-python/commit/94c4de88b9d202d780c4dfbee6db138d7a663373)) ## 0.7.6 (2023-11-28) Full Changelog: [v0.7.5...v0.7.6](https://github.com/anthropics/anthropic-sdk-python/compare/v0.7.5...v0.7.6) ### Chores * **deps:** bump mypy to v1.7.1 ([#256](https://github.com/anthropics/anthropic-sdk-python/issues/256)) ([02d4ed8](https://github.com/anthropics/anthropic-sdk-python/commit/02d4ed8ae8e4fb9221fc9bfb5f45357ed239de5e)) ## 0.7.5 (2023-11-24) Full Changelog: [v0.7.4...v0.7.5](https://github.com/anthropics/anthropic-sdk-python/compare/v0.7.4...v0.7.5) ### Chores * **internal:** revert recent options change ([#252](https://github.com/anthropics/anthropic-sdk-python/issues/252)) ([d60d5c3](https://github.com/anthropics/anthropic-sdk-python/commit/d60d5c33aec2964b3dbbc69bdf8556b4100a684f)) * **internal:** send more detailed x-stainless headers ([#254](https://github.com/anthropics/anthropic-sdk-python/issues/254)) ([a268d4b](https://github.com/anthropics/anthropic-sdk-python/commit/a268d4bf4f2fb17707c5328e1ba25e623e7b9b78)) ## 0.7.4 (2023-11-23) Full Changelog: [v0.7.3...v0.7.4](https://github.com/anthropics/anthropic-sdk-python/compare/v0.7.3...v0.7.4) ### Chores * **internal:** options updates ([#248](https://github.com/anthropics/anthropic-sdk-python/issues/248)) ([5a3b236](https://github.com/anthropics/anthropic-sdk-python/commit/5a3b2362af3b7556babb99095df88443c56579ec)) ## 0.7.3 (2023-11-21) Full Changelog: [v0.7.2...v0.7.3](https://github.com/anthropics/anthropic-sdk-python/compare/v0.7.2...v0.7.3) ### Bug Fixes * **client:** attempt to parse unknown json content types ([#243](https://github.com/anthropics/anthropic-sdk-python/issues/243)) ([9fc275f](https://github.com/anthropics/anthropic-sdk-python/commit/9fc275f606b52690d5ccda78c72a6fded68ccb1e)) ### Chores * **client:** improve copy method ([#246](https://github.com/anthropics/anthropic-sdk-python/issues/246)) ([c84563f](https://github.com/anthropics/anthropic-sdk-python/commit/c84563fc69554b322d2a4254b6470ba7819689c3)) * **package:** add license classifier metadata ([#247](https://github.com/anthropics/anthropic-sdk-python/issues/247)) ([500d0ca](https://github.com/anthropics/anthropic-sdk-python/commit/500d0ca1e4d08f8c6b5d58071f438de9e1a31217)) ## 0.7.2 (2023-11-17) Full Changelog: [v0.7.1...v0.7.2](https://github.com/anthropics/anthropic-sdk-python/compare/v0.7.1...v0.7.2) ### Chores * **internal:** update type hint for helper function ([#241](https://github.com/anthropics/anthropic-sdk-python/issues/241)) ([3179104](https://github.com/anthropics/anthropic-sdk-python/commit/31791042c52e825d1763123b14f44b9e68cc3466)) ## 0.7.1 (2023-11-16) Full Changelog: [v0.7.0...v0.7.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.7.0...v0.7.1) ### Documentation * **readme:** minor updates ([#238](https://github.com/anthropics/anthropic-sdk-python/issues/238)) ([c40c4e1](https://github.com/anthropics/anthropic-sdk-python/commit/c40c4e1c9979f62a485df52bf51a5d730c3af38f)) ## 0.7.0 (2023-11-15) Full Changelog: [v0.6.0...v0.7.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.6.0...v0.7.0) ### Features * **client:** support reading the base url from an env variable ([#237](https://github.com/anthropics/anthropic-sdk-python/issues/237)) ([dd91bfd](https://github.com/anthropics/anthropic-sdk-python/commit/dd91bfd278f4e2e76b2f194098f34070fd5a3ff9)) ### Bug Fixes * **client:** correctly flush the stream response body ([#230](https://github.com/anthropics/anthropic-sdk-python/issues/230)) ([a60d543](https://github.com/anthropics/anthropic-sdk-python/commit/a60d54331f8f6d28bf57dc979d4393759f5e1534)) * **client:** retry if SSLWantReadError occurs in the async client ([#233](https://github.com/anthropics/anthropic-sdk-python/issues/233)) ([33b553a](https://github.com/anthropics/anthropic-sdk-python/commit/33b553a8de5d45273ca9f335c59a263136385f14)) * **client:** serialise pydantic v1 default fields correctly in params ([#232](https://github.com/anthropics/anthropic-sdk-python/issues/232)) ([d5e70e8](https://github.com/anthropics/anthropic-sdk-python/commit/d5e70e8b803c96c8640508b31773b8b9d827d903)) * **models:** mark unknown fields as set in pydantic v1 ([#231](https://github.com/anthropics/anthropic-sdk-python/issues/231)) ([4ce7a1e](https://github.com/anthropics/anthropic-sdk-python/commit/4ce7a1e676023984be80fe0eacb1a0223780886c)) ### Chores * **internal:** fix devcontainer interpeter path ([#235](https://github.com/anthropics/anthropic-sdk-python/issues/235)) ([7f92e25](https://github.com/anthropics/anthropic-sdk-python/commit/7f92e25d6fa15bed799994d173ad62bcf60e5b3b)) * **internal:** fix typo in NotGiven docstring ([#234](https://github.com/anthropics/anthropic-sdk-python/issues/234)) ([ce5cccc](https://github.com/anthropics/anthropic-sdk-python/commit/ce5cccc9bc8482e4e3f6af034892a347eb2b52fc)) ### Documentation * fix code comment typo ([#236](https://github.com/anthropics/anthropic-sdk-python/issues/236)) ([7ef0464](https://github.com/anthropics/anthropic-sdk-python/commit/7ef0464724346d930ff1580526fd70b592759641)) * reword package description ([#228](https://github.com/anthropics/anthropic-sdk-python/issues/228)) ([c18e5ed](https://github.com/anthropics/anthropic-sdk-python/commit/c18e5ed77700bc98ba1c85638a503e9e0a35afb7)) ## 0.6.0 (2023-11-08) Full Changelog: [v0.5.1...v0.6.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.5.1...v0.6.0) ### Features * **client:** adjust retry behavior to be exponential backoff ([#205](https://github.com/anthropics/anthropic-sdk-python/issues/205)) ([c8a4119](https://github.com/anthropics/anthropic-sdk-python/commit/c8a4119661c8ff74c7efa308963c2f187728a46f)) * **client:** allow binary returns ([#217](https://github.com/anthropics/anthropic-sdk-python/issues/217)) ([159ddd6](https://github.com/anthropics/anthropic-sdk-python/commit/159ddd69e6c438baf9abb1e518d0c2467c8f952c)) * **client:** improve file upload types ([#204](https://github.com/anthropics/anthropic-sdk-python/issues/204)) ([d85d1e0](https://github.com/anthropics/anthropic-sdk-python/commit/d85d1e04e36a90d43d134992ff4a5b1589aa6e0a)) * **client:** support accessing raw response objects ([#211](https://github.com/anthropics/anthropic-sdk-python/issues/211)) ([ebe8e4a](https://github.com/anthropics/anthropic-sdk-python/commit/ebe8e4a274f21d73cbc2fbb94fe56172f335cbd2)) * **client:** support passing BaseModels to request params at runtime ([#218](https://github.com/anthropics/anthropic-sdk-python/issues/218)) ([9f04ea6](https://github.com/anthropics/anthropic-sdk-python/commit/9f04ea6cf4a68e2ce65e8e00448b4d3de18a8dec)) * **client:** support passing chunk size for binary responses ([#227](https://github.com/anthropics/anthropic-sdk-python/issues/227)) ([c88f01e](https://github.com/anthropics/anthropic-sdk-python/commit/c88f01ed17b505e3e8a30c8a6adc9231e096b3e2)) * **client:** support passing httpx.Timeout to method timeout argument ([#222](https://github.com/anthropics/anthropic-sdk-python/issues/222)) ([ef58166](https://github.com/anthropics/anthropic-sdk-python/commit/ef58166e0fac68256ca8154792d2157698ed6a9d)) * **github:** include a devcontainer setup ([#216](https://github.com/anthropics/anthropic-sdk-python/issues/216)) ([c9fee19](https://github.com/anthropics/anthropic-sdk-python/commit/c9fee192863fa5f894035ce3e1cf52a78b56895d)) * **package:** add classifiers ([#214](https://github.com/anthropics/anthropic-sdk-python/issues/214)) ([380967e](https://github.com/anthropics/anthropic-sdk-python/commit/380967e515279482e7a93570f172f52324f8aa26)) ### Bug Fixes * **binaries:** don't synchronously block in astream_to_file ([#219](https://github.com/anthropics/anthropic-sdk-python/issues/219)) ([2a2a617](https://github.com/anthropics/anthropic-sdk-python/commit/2a2a617d6862eb83b8a671acad08825c3a20d11b)) * prevent TypeError in Python 3.8 (ABC is not subscriptable) ([#221](https://github.com/anthropics/anthropic-sdk-python/issues/221)) ([893e885](https://github.com/anthropics/anthropic-sdk-python/commit/893e885859b5fb94d7673bfa9ad0a04434fec196)) ### Chores * **docs:** fix github links ([#225](https://github.com/anthropics/anthropic-sdk-python/issues/225)) ([dfa9935](https://github.com/anthropics/anthropic-sdk-python/commit/dfa99352291b15b8c885eb558c8b738b26d33373)) * **internal:** fix some typos ([#223](https://github.com/anthropics/anthropic-sdk-python/issues/223)) ([9038193](https://github.com/anthropics/anthropic-sdk-python/commit/9038193db52612f756194fd735aab899bed0931f)) * **internal:** improve github devcontainer setup ([#226](https://github.com/anthropics/anthropic-sdk-python/issues/226)) ([3cd90ab](https://github.com/anthropics/anthropic-sdk-python/commit/3cd90abe2c57375438a4209e31253f758f408b17)) * **internal:** minor restructuring of base client ([#213](https://github.com/anthropics/anthropic-sdk-python/issues/213)) ([60dc609](https://github.com/anthropics/anthropic-sdk-python/commit/60dc609aa9c4b01b88d9c7e8d1eb35bf9561f210)) * **internal:** remove unused int/float conversion ([#220](https://github.com/anthropics/anthropic-sdk-python/issues/220)) ([a6bf20d](https://github.com/anthropics/anthropic-sdk-python/commit/a6bf20d8cb64f13618c3122f8285d240840884f8)) * **internal:** require explicit overrides ([#210](https://github.com/anthropics/anthropic-sdk-python/issues/210)) ([72f4339](https://github.com/anthropics/anthropic-sdk-python/commit/72f4339749f144e75e0e7dc0a7b2bb26f728044e)) ### Documentation * fix github links ([#215](https://github.com/anthropics/anthropic-sdk-python/issues/215)) ([8cbed15](https://github.com/anthropics/anthropic-sdk-python/commit/8cbed150d6e8f6ac8de8962e169ca46cdd0643c5)) * improve to dictionary example ([#207](https://github.com/anthropics/anthropic-sdk-python/issues/207)) ([5e32c20](https://github.com/anthropics/anthropic-sdk-python/commit/5e32c201f7017c2d4aa7416d1a7de3f0c5247fcc)) ## 0.5.1 (2023-10-20) Full Changelog: [v0.5.0...v0.5.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.5.0...v0.5.1) ### Chores * **internal:** bump mypy ([#203](https://github.com/anthropics/anthropic-sdk-python/issues/203)) ([aa9a67e](https://github.com/anthropics/anthropic-sdk-python/commit/aa9a67e9286146e088af74ded73e3b4d6dde9c7b)) * **internal:** bump pyright ([#202](https://github.com/anthropics/anthropic-sdk-python/issues/202)) ([f96f5f7](https://github.com/anthropics/anthropic-sdk-python/commit/f96f5f75e4b54481bceb033f432f2911355f02e4)) * **internal:** update gitignore ([#199](https://github.com/anthropics/anthropic-sdk-python/issues/199)) ([b92fa57](https://github.com/anthropics/anthropic-sdk-python/commit/b92fa57ac997d80166dd758e1bc9bb58b217c572)) ## 0.5.0 (2023-10-18) Full Changelog: [v0.4.1...v0.5.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.4.1...v0.5.0) ### Features * **client:** support passing httpx.URL instances to base_url ([#197](https://github.com/anthropics/anthropic-sdk-python/issues/197)) ([fe61308](https://github.com/anthropics/anthropic-sdk-python/commit/fe61308baa7d11993e72b3a282633a24fb4e61e4)) ### Chores * **internal:** improve publish script ([#196](https://github.com/anthropics/anthropic-sdk-python/issues/196)) ([7c92b90](https://github.com/anthropics/anthropic-sdk-python/commit/7c92b90864f9510e7cbb68c6b703eec7fd4b7b28)) * **internal:** migrate from Poetry to Rye ([#194](https://github.com/anthropics/anthropic-sdk-python/issues/194)) ([1dd605e](https://github.com/anthropics/anthropic-sdk-python/commit/1dd605e7daf6f8542cb0ff5f5af4f161153f239a)) * **internal:** update gitignore ([#198](https://github.com/anthropics/anthropic-sdk-python/issues/198)) ([4c210b7](https://github.com/anthropics/anthropic-sdk-python/commit/4c210b75ce9fee9a781fbcbab8711409de2d9eea)) ## 0.4.1 (2023-10-16) Full Changelog: [v0.4.0...v0.4.1](https://github.com/anthropics/anthropic-sdk-python/compare/v0.4.0...v0.4.1) ### Bug Fixes * **client:** accept io.IOBase instances in file params ([#190](https://github.com/anthropics/anthropic-sdk-python/issues/190)) ([5da5f0c](https://github.com/anthropics/anthropic-sdk-python/commit/5da5f0cbfddfc04fc3b1c86dcbd04aa9d5f1b1e4)) * **streaming:** add additional overload for ambiguous stream param ([#185](https://github.com/anthropics/anthropic-sdk-python/issues/185)) ([794dc4d](https://github.com/anthropics/anthropic-sdk-python/commit/794dc4daa1c7ccea4157eed725e47409fe7f23dc)) ### Chores * **internal:** cleanup some redundant code ([#188](https://github.com/anthropics/anthropic-sdk-python/issues/188)) ([cb0bd8c](https://github.com/anthropics/anthropic-sdk-python/commit/cb0bd8c4e7f311c547674ee3c39dec23829e9422)) * **internal:** enable lint rule ([#187](https://github.com/anthropics/anthropic-sdk-python/issues/187)) ([123b5c1](https://github.com/anthropics/anthropic-sdk-python/commit/123b5c196ef87b4293fb5cbee2c6f3e6da739df8)) ### Documentation * organisation -> organization (UK to US English) ([#192](https://github.com/anthropics/anthropic-sdk-python/issues/192)) ([901a330](https://github.com/anthropics/anthropic-sdk-python/commit/901a33004bcf9d2ff10c742924a70a919ae1cfef)) ## 0.4.0 (2023-10-13) Full Changelog: [v0.3.14...v0.4.0](https://github.com/anthropics/anthropic-sdk-python/compare/v0.3.14...v0.4.0) ### Features * **client:** add logging setup ([#177](https://github.com/anthropics/anthropic-sdk-python/issues/177)) ([a5f87ad](https://github.com/anthropics/anthropic-sdk-python/commit/a5f87ad433ab8332b7c253160bedba0adcc2b3e2)) ### Bug Fixes * **client:** correctly handle arguments with env vars ([#178](https://github.com/anthropics/anthropic-sdk-python/issues/178)) ([91a0e2a](https://github.com/anthropics/anthropic-sdk-python/commit/91a0e2a9e436f47f46e36c7072c917a62a08ce16)) ### Chores * add case insensitive get header function ([#182](https://github.com/anthropics/anthropic-sdk-python/issues/182)) ([708fd02](https://github.com/anthropics/anthropic-sdk-python/commit/708fd027b113911f2e1801c4213f6700a9399aa7)) * update comment ([#183](https://github.com/anthropics/anthropic-sdk-python/issues/183)) ([649d6f4](https://github.com/anthropics/anthropic-sdk-python/commit/649d6f468a09648e91ae69cbc07420f055029edf)) * update README ([#174](https://github.com/anthropics/anthropic-sdk-python/issues/174)) ([bb581b5](https://github.com/anthropics/anthropic-sdk-python/commit/bb581b585e776dee50d05f3fcd2853483c3c2ac1)) ### Documentation * minor readme reordering ([#180](https://github.com/anthropics/anthropic-sdk-python/issues/180)) ([92345e3](https://github.com/anthropics/anthropic-sdk-python/commit/92345e31eca3036ab571c57f8c76424a578b55f4)) ### Refactors * **test:** refactor authentication tests ([#175](https://github.com/anthropics/anthropic-sdk-python/issues/175)) ([c82da53](https://github.com/anthropics/anthropic-sdk-python/commit/c82da53502dbb884fc92f1da094a968c9237927b)) ## 0.3.14 (2023-10-11) Full Changelog: [v0.3.13...v0.3.14](https://github.com/anthropics/anthropic-sdk-python/compare/v0.3.13...v0.3.14) ### Features * **client:** add forwards-compatible pydantic methods ([#171](https://github.com/anthropics/anthropic-sdk-python/issues/171)) ([4c5289e](https://github.com/anthropics/anthropic-sdk-python/commit/4c5289eb8519ca9a53e9483422237aa25944f8d8)) * **client:** add support for passing in a httpx client ([#173](https://github.com/anthropics/anthropic-sdk-python/issues/173)) ([25046c4](https://github.com/anthropics/anthropic-sdk-python/commit/25046c4fbc6f9d343e3b1f21024cf3982ac48c35)) * **client:** handle retry-after header with a date format ([#168](https://github.com/anthropics/anthropic-sdk-python/issues/168)) ([afeabf1](https://github.com/anthropics/anthropic-sdk-python/commit/afeabf13aa5795a7fadd141e53ec81eadbce099a)) * **client:** retry on 408 Request Timeout ([#155](https://github.com/anthropics/anthropic-sdk-python/issues/155)) ([46386f8](https://github.com/anthropics/anthropic-sdk-python/commit/46386f8f60223f45bc133ddfcfda8d9ca9da26a8)) * **package:** export a root error type ([#163](https://github.com/anthropics/anthropic-sdk-python/issues/163)) ([e7aa3e7](https://github.com/anthropics/anthropic-sdk-python/commit/e7aa3e7785ae511fa35a68ac72079a6230ca84f3)) * **types:** improve params type names ([#160](https://github.com/anthropics/anthropic-sdk-python/issues/160)) ([43544a6](https://github.com/anthropics/anthropic-sdk-python/commit/43544a62c8410061c1a50282f4c45d029db7779b)) ### Bug Fixes * **client:** don't error by default for unexpected content types ([#161](https://github.com/anthropics/anthropic-sdk-python/issues/161)) ([76cfcf9](https://github.com/anthropics/anthropic-sdk-python/commit/76cfcf91172f9804056a7d5c1ec99666ad5991a2)) * **client:** properly configure model set fields ([#154](https://github.com/anthropics/anthropic-sdk-python/issues/154)) ([da6ccb1](https://github.com/anthropics/anthropic-sdk-python/commit/da6ccb10a38e862153871a540cb75af0afdaefb3)) ### Chores * **internal:** add helpers ([#156](https://github.com/anthropics/anthropic-sdk-python/issues/156)) ([00f5a19](https://github.com/anthropics/anthropic-sdk-python/commit/00f5a19c9393f6238759faea40405e60b2054da3)) * **internal:** move error classes from _base_exceptions to _exceptions (âš ï¸ breaking) ([#162](https://github.com/anthropics/anthropic-sdk-python/issues/162)) ([329b307](https://github.com/anthropics/anthropic-sdk-python/commit/329b307c205435d367c0d4b29b252be807c61c68)) * **tests:** improve raw response test ([#166](https://github.com/anthropics/anthropic-sdk-python/issues/166)) ([8042473](https://github.com/anthropics/anthropic-sdk-python/commit/8042473bd73faa0b819c27a68bfc19b918361461)) ### Documentation * add some missing inline documentation ([#151](https://github.com/anthropics/anthropic-sdk-python/issues/151)) ([1f98257](https://github.com/anthropics/anthropic-sdk-python/commit/1f9825775d58ed8a62b000caaddd622ed4ba3fd2)) * update readme ([#172](https://github.com/anthropics/anthropic-sdk-python/issues/172)) ([351095b](https://github.com/anthropics/anthropic-sdk-python/commit/351095b189b111a74e9e1825ce5b6da6673a1635)) ## 0.3.13 (2023-09-11) Full Changelog: [v0.3.12...v0.3.13](https://github.com/anthropics/anthropic-sdk-python/compare/v0.3.12...v0.3.13) ### Features * **types:** de-duplicate nested streaming params types ([#141](https://github.com/anthropics/anthropic-sdk-python/issues/141)) ([f76f053](https://github.com/anthropics/anthropic-sdk-python/commit/f76f05320df3059d57ed57153f30be3a8d91fddf)) ### Bug Fixes * **client:** properly handle optional file params ([#142](https://github.com/anthropics/anthropic-sdk-python/issues/142)) ([11196b7](https://github.com/anthropics/anthropic-sdk-python/commit/11196b757c4ef334f8b6db069ecfc6a57c200389)) ### Chores * **internal:** add `pydantic.generics` import for compatibility ([#135](https://github.com/anthropics/anthropic-sdk-python/issues/135)) ([951446d](https://github.com/anthropics/anthropic-sdk-python/commit/951446dbd48e0e5b674fd988865f3aef60c86720)) * **internal:** minor restructuring ([#137](https://github.com/anthropics/anthropic-sdk-python/issues/137)) ([e601206](https://github.com/anthropics/anthropic-sdk-python/commit/e60120670adbc404b06b0fef9e40134929bc7bbd)) * **internal:** minor update ([#145](https://github.com/anthropics/anthropic-sdk-python/issues/145)) ([6a505ef](https://github.com/anthropics/anthropic-sdk-python/commit/6a505ef95523b725a8fdcba71faf9719292e5085)) * **internal:** update base client ([#143](https://github.com/anthropics/anthropic-sdk-python/issues/143)) ([8e0dca4](https://github.com/anthropics/anthropic-sdk-python/commit/8e0dca4fe290833f2aa8b25d6c80b0154ea2a703)) * **internal:** update lock file ([#147](https://github.com/anthropics/anthropic-sdk-python/issues/147)) ([a72b5ca](https://github.com/anthropics/anthropic-sdk-python/commit/a72b5ca4caa8963961d97e5d689393953e00c49b)) * **internal:** update pyright ([#149](https://github.com/anthropics/anthropic-sdk-python/issues/149)) ([9661f94](https://github.com/anthropics/anthropic-sdk-python/commit/9661f941ede82f0023c47b0d9c9beacbd5bbb703)) * **internal:** updates ([#148](https://github.com/anthropics/anthropic-sdk-python/issues/148)) ([9f7fbbc](https://github.com/anthropics/anthropic-sdk-python/commit/9f7fbbcd36ed2accb3a59275255f84200ab17b66)) ### Documentation * **readme:** add link to api.md ([#146](https://github.com/anthropics/anthropic-sdk-python/issues/146)) ([1fcb30a](https://github.com/anthropics/anthropic-sdk-python/commit/1fcb30ae85153d4ab34935a86dcaf0d0fc4470e9)) * **readme:** reference pydantic helpers ([#138](https://github.com/anthropics/anthropic-sdk-python/issues/138)) ([ccaab99](https://github.com/anthropics/anthropic-sdk-python/commit/ccaab990df18404db636f206575ed0548e9420e9)) ## 0.3.12 (2023-08-29) Full Changelog: [v0.3.11...v0.3.12](https://github.com/anthropics/anthropic-sdk-python/compare/v0.3.11...v0.3.12) ### Chores * **ci:** setup workflows to create releases and release PRs ([#130](https://github.com/anthropics/anthropic-sdk-python/issues/130)) ([8f1048b](https://github.com/anthropics/anthropic-sdk-python/commit/8f1048b0f25116ecf4cdedec651a5f8f38fe0d72)) * **internal:** use shared params references ([#133](https://github.com/anthropics/anthropic-sdk-python/issues/133)) ([feaf6aa](https://github.com/anthropics/anthropic-sdk-python/commit/feaf6aa84e83a12e2bd51d78141c2626bfd228e6)) ## [0.3.11](https://github.com/anthropics/anthropic-sdk-python/compare/v0.3.10...v0.3.11) (2023-08-26) ### Documentation * **readme:** make print statements in streaming examples flush ([#123](https://github.com/anthropics/anthropic-sdk-python/issues/123)) ([d24dfaf](https://github.com/anthropics/anthropic-sdk-python/commit/d24dfaffbfd7e82c20c7d846eeddd2af1404e26b)) ### Chores * **internal:** update anyio ([#125](https://github.com/anthropics/anthropic-sdk-python/issues/125)) ([34c7fa1](https://github.com/anthropics/anthropic-sdk-python/commit/34c7fa16006cb2842ccc59c416441b930ed855e7)) ## [0.3.10](https://github.com/anthropics/anthropic-sdk-python/compare/v0.3.9...v0.3.10) (2023-08-16) ### Features * add support for Pydantic v2 ([#121](https://github.com/anthropics/anthropic-sdk-python/issues/121)) ([cafa9be](https://github.com/anthropics/anthropic-sdk-python/commit/cafa9beef10afcec8cc537946c0ee5574f1c96e7)) * allow a default timeout to be set for clients ([#117](https://github.com/anthropics/anthropic-sdk-python/issues/117)) ([a115d2c](https://github.com/anthropics/anthropic-sdk-python/commit/a115d2c978ee6bbe749c55851833e52b8671e343)) ### Chores * assign default reviewers to release PRs ([#119](https://github.com/anthropics/anthropic-sdk-python/issues/119)) ([029a9e1](https://github.com/anthropics/anthropic-sdk-python/commit/029a9e157a4831203f0599d25a223a775fb937a6)) * **internal:** minor formatting change ([#120](https://github.com/anthropics/anthropic-sdk-python/issues/120)) ([7f2f54a](https://github.com/anthropics/anthropic-sdk-python/commit/7f2f54a76dbf182ce13a8d36741421a9c5cf2001)) ## [0.3.9](https://github.com/anthropics/anthropic-sdk-python/compare/v0.3.8...v0.3.9) (2023-08-12) ### Features * **docs:** remove extraneous space in examples ([#109](https://github.com/anthropics/anthropic-sdk-python/issues/109)) ([6d5c1f7](https://github.com/anthropics/anthropic-sdk-python/commit/6d5c1f72aea3156a4773ffb25ac00cdd75191652)) ### Bug Fixes * **docs:** correct async imports ([1ea1bf3](https://github.com/anthropics/anthropic-sdk-python/commit/1ea1bf342c8d54ff9c37ba1d1c591b4fd3362868)) ### Documentation * **readme:** remove beta status + document versioning policy ([#102](https://github.com/anthropics/anthropic-sdk-python/issues/102)) ([2f0a0f9](https://github.com/anthropics/anthropic-sdk-python/commit/2f0a0f9aeac863c18cfc9fff83b3c7675447f408)) ### Chores * **deps:** bump typing-extensions to 4.5 ([#112](https://github.com/anthropics/anthropic-sdk-python/issues/112)) ([f903269](https://github.com/anthropics/anthropic-sdk-python/commit/f9032699e6610363f0490026fb65e05f2283f782)) * **docs:** remove trailing spaces ([#113](https://github.com/anthropics/anthropic-sdk-python/issues/113)) ([e876a6b](https://github.com/anthropics/anthropic-sdk-python/commit/e876a6b957aa2f73f63b4b314942461e4f72de57)) * **internal:** bump pytest-asyncio ([#114](https://github.com/anthropics/anthropic-sdk-python/issues/114)) ([679ecd0](https://github.com/anthropics/anthropic-sdk-python/commit/679ecd0c3c365c70c1c677e5b7e33281cf36fafe)) * **internal:** update mypy to v1.4.1 ([#100](https://github.com/anthropics/anthropic-sdk-python/issues/100)) ([f615753](https://github.com/anthropics/anthropic-sdk-python/commit/f615753a4b6413f1e6af69e3698d19e74bafcdca)) * **internal:** update ruff to v0.0.282 ([#103](https://github.com/anthropics/anthropic-sdk-python/issues/103)) ([9db4b34](https://github.com/anthropics/anthropic-sdk-python/commit/9db4b34844c5bfee65ab4e1ad448ae13d6469e5f)) ## [0.3.8](https://github.com/anthropics/anthropic-sdk-python/compare/v0.3.7...v0.3.8) (2023-08-01) ### Features * **client:** add constants to client instances as well ([#95](https://github.com/anthropics/anthropic-sdk-python/issues/95)) ([d0fbe33](https://github.com/anthropics/anthropic-sdk-python/commit/d0fbe33bd9bab72438c2b80b48b80908bd994797)) ### Chores * **internal:** bump pyright ([#94](https://github.com/anthropics/anthropic-sdk-python/issues/94)) ([d2872dc](https://github.com/anthropics/anthropic-sdk-python/commit/d2872dcc19c409cb7383e70f0472378a0ae86ff0)) * **internal:** make demo example runnable and more portable ([#92](https://github.com/anthropics/anthropic-sdk-python/issues/92)) ([dea1aa2](https://github.com/anthropics/anthropic-sdk-python/commit/dea1aa2f4b699043780b61d92e93c0f2a8fe59bd)) ### Documentation * **readme:** add token counting reference ([#96](https://github.com/anthropics/anthropic-sdk-python/issues/96)) ([79a339e](https://github.com/anthropics/anthropic-sdk-python/commit/79a339e962aa51cf79064af787ce11bd7984e0e4)) ## [0.3.7](https://github.com/anthropics/anthropic-sdk-python/compare/v0.3.6...v0.3.7) (2023-07-29) ### Features * **client:** add client close handlers ([#89](https://github.com/anthropics/anthropic-sdk-python/issues/89)) ([2520a03](https://github.com/anthropics/anthropic-sdk-python/commit/2520a034eed3e218f25f369783ebd09e2763e803)) ### Bug Fixes * **client:** correctly handle environment variable access ([aa53754](https://github.com/anthropics/anthropic-sdk-python/commit/aa53754c71cdfc31f236b41222752f3a58602061)) ### Documentation * **readme:** use `client` everywhere for consistency ([0ff8924](https://github.com/anthropics/anthropic-sdk-python/commit/0ff89245f4aa7ca3f6282827ab7c5cca3be534fb)) ### Chores * **internal:** minor refactoring of client instantiation ([adf9158](https://github.com/anthropics/anthropic-sdk-python/commit/adf91584ade62e3a4c2fbef011a62cf9284db931)) * **internal:** minor reformatting of code ([#90](https://github.com/anthropics/anthropic-sdk-python/issues/90)) ([1175572](https://github.com/anthropics/anthropic-sdk-python/commit/1175572db453b681b5aa8469d09f9400ddcd4946)) ## [0.3.6](https://github.com/anthropics/anthropic-sdk-python/compare/v0.3.5...v0.3.6) (2023-07-22) ### Documentation * **readme:** reference "client" in errors section and add missing import ([#79](https://github.com/anthropics/anthropic-sdk-python/issues/79)) ([ddc81cf](https://github.com/anthropics/anthropic-sdk-python/commit/ddc81cf0c1593ed9d4855e27fbcc0a393cf2c3a2)) ## [0.3.5](https://github.com/anthropics/anthropic-sdk-python/compare/v0.3.4...v0.3.5) (2023-07-19) ### Features * add flexible enum to model param ([#75](https://github.com/anthropics/anthropic-sdk-python/issues/75)) ([d16bb45](https://github.com/anthropics/anthropic-sdk-python/commit/d16bb45c49f4f401bd33ab57d5f9a586bd1e9a01)) ### Documentation * **examples:** bump model to claude-2 in example scripts ([#67](https://github.com/anthropics/anthropic-sdk-python/issues/67)) ([cd68de2](https://github.com/anthropics/anthropic-sdk-python/commit/cd68de2c5351fca85784e44d6eee5d90c835f64b)) ### Chores * **internal:** add `codegen.log` to `.gitignore` ([#72](https://github.com/anthropics/anthropic-sdk-python/issues/72)) ([d9b7e30](https://github.com/anthropics/anthropic-sdk-python/commit/d9b7e30b26235860fe9e7e3053171615173e32ca)) ## [0.3.4](https://github.com/anthropics/anthropic-sdk-python/compare/v0.3.3...v0.3.4) (2023-07-11) ### Chores * **package:** pin major versions of dependencies ([#59](https://github.com/anthropics/anthropic-sdk-python/issues/59)) ([3a75464](https://github.com/anthropics/anthropic-sdk-python/commit/3a754645aa7381d160e985451f385ce231a66904)) ### Documentation * **api:** reference claude-2 ([#61](https://github.com/anthropics/anthropic-sdk-python/issues/61)) ([91ece29](https://github.com/anthropics/anthropic-sdk-python/commit/91ece29cd6ae9ba9a060bee8b55fb62ddc1b69ac)) * **readme:** update examples to use claude-2 ([#65](https://github.com/anthropics/anthropic-sdk-python/issues/65)) ([7e4714c](https://github.com/anthropics/anthropic-sdk-python/commit/7e4714c19a64b2da74531ee7c051a5eef55d693c)) anthropic-sdk-python-0.120.2/CONTRIBUTING.md000066400000000000000000000111551523216435200202000ustar00rootroot00000000000000## Contributing to documentation The documentation for this SDK lives at [platform.claude.com/docs/en/api/sdks/python](https://platform.claude.com/docs/en/api/sdks/python). To suggest changes, open an issue. ## Setting up the environment ### With `uv` We use [uv](https://docs.astral.sh/uv/) to manage dependencies because it will automatically provision a Python environment with the expected Python version. To set it up, run: ```sh $ ./scripts/bootstrap ``` Or [install uv manually](https://docs.astral.sh/uv/getting-started/installation/) and run: ```sh $ uv sync --all-extras ``` You can then run scripts using `uv run python script.py` or by manually activating the virtual environment: ```sh # manually activate - https://docs.python.org/3/library/venv.html#how-venvs-work $ source .venv/bin/activate # now you can omit the `uv run` prefix $ python script.py ``` ### Without `uv` Alternatively if you don't want to install `uv`, you can stick with the standard `pip` setup by ensuring you have the Python version specified in `.python-version`, create a virtual environment however you desire and then install dependencies using this command: ```sh $ pip install -r requirements-dev.lock ``` ## Modifying/Adding code Most of the SDK is generated code. Modifications to code will be persisted between generations, but may result in merge conflicts between manual patches and changes from the generator. The generator will never modify the contents of the `src/anthropic/lib/` and `examples/` directories. ## Adding and running examples All files in the `examples/` directory are not modified by the generator and can be freely edited or added to. ```py # add an example to examples/.py #!/usr/bin/env -S uv run python … ``` ```sh $ chmod +x examples/.py # run the example against your api $ ./examples/.py ``` ## Using the repository from source If you’d like to use the repository from source, you can either install from git or link to a cloned repository: To install via git: ```sh $ pip install git+ssh://git@github.com/anthropics/anthropic-sdk-python.git ``` Alternatively, you can build from source and install the wheel file: Building this package will create two files in the `dist/` directory, a `.tar.gz` containing the source files and a `.whl` that can be used to install the package efficiently. To create a distributable version of the library, all you have to do is run this command: ```sh $ uv build # or $ python -m build ``` Then to install: ```sh $ pip install ./path-to-wheel-file.whl ``` ## Running tests Most tests require you to [set up a mock server](https://github.com/dgellow/steady) against the OpenAPI spec to run the tests. ```sh $ ./scripts/mock ``` ```sh $ ./scripts/test ``` ### Snapshots Some tests use [inline-snapshot](https://15r10nk.github.io/inline-snapshot/latest/). To update them after making changes, rerun the tests with the `--inline-snapshot=fix` and `-n0` options: ```bash ./scripts/test --inline-snapshot=fix -n0 ``` > [!NOTE] > `inline-snapshot` is incompatible with [pytest-xdist](https://github.com/pytest-dev/pytest-xdist), so you need to disable parallel execution `(-n0)` when using the `--inline-snapshot` option. In addition, some tests capture snapshots of the HTTP requests they make. To refresh these snapshots, run the tests with the `--http-record` flag: ```bash ./scripts/test --inline-snapshot=fix --http-record -n0 ``` > [!NOTE] > Sometimes it makes sense to update only the inline snapshots `(--inline-snapshot=fix)` without refreshing the HTTP snapshots `(--http-record)`. > This is useful when the endpoint hasn't changed, but your code handles the response differently and the assertions need updating. ## Linting and formatting This repository uses [ruff](https://github.com/astral-sh/ruff) and [black](https://github.com/psf/black) to format the code in the repository. To lint: ```sh $ ./scripts/lint ``` To format and fix all ruff issues automatically: ```sh $ ./scripts/format ``` ## Publishing and releases Changes made to this repository via the automated release PR pipeline should publish to PyPI automatically. If the changes aren't made through the automated pipeline, you may want to make releases manually. ### Publish with a GitHub workflow You can release to package managers by using [the `Publish PyPI` GitHub action](https://www.github.com/anthropics/anthropic-sdk-python/actions/workflows/publish-pypi.yml). This requires a setup organization or repository secret to be set up. ### Publish manually If you need to manually release a package, you can run the `bin/publish-pypi` script with a `PYPI_TOKEN` set on the environment. anthropic-sdk-python-0.120.2/LICENSE000066400000000000000000000020401523216435200167450ustar00rootroot00000000000000Copyright 2023 Anthropic, PBC. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. anthropic-sdk-python-0.120.2/README.md000066400000000000000000000020541523216435200172240ustar00rootroot00000000000000# Claude SDK for Python [![PyPI version](https://img.shields.io/pypi/v/anthropic.svg)](https://pypi.org/project/anthropic/) The Claude SDK for Python provides access to the [Claude API](https://docs.anthropic.com/en/api/) from Python applications. ## Documentation Full documentation is available at **[platform.claude.com/docs/en/api/sdks/python](https://platform.claude.com/docs/en/api/sdks/python)**. ## Installation ```sh pip install anthropic ``` ## Getting started ```python import os from anthropic import Anthropic client = Anthropic( api_key=os.environ.get("ANTHROPIC_API_KEY"), # This is the default and can be omitted ) message = client.messages.create( max_tokens=1024, messages=[ { "role": "user", "content": "Hello, Claude", } ], model="claude-opus-4-6", ) print(message.content) ``` ## Requirements Python 3.9+ ## Contributing See [CONTRIBUTING.md](./CONTRIBUTING.md). ## License This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. anthropic-sdk-python-0.120.2/SECURITY.md000066400000000000000000000014051523216435200175350ustar00rootroot00000000000000# Security Policy Thank you for helping us keep the SDKs and systems they interact with secure. ## Reporting Security Issues This SDK is maintained by [Anthropic](https://www.anthropic.com/). The security of our systems and user data is Anthropic’s top priority. We appreciate the work of security researchers acting in good faith in identifying and reporting potential vulnerabilities. Our security program is managed on HackerOne and we ask that any validated vulnerability in this functionality be reported through their [submission form](https://hackerone.com/4f1f16ba-10d3-4d09-9ecc-c721aad90f24/embedded_submissions/new). ## Anthropic Bug Bounty Our Bug Bounty Program Guidelines are defined on our [HackerOne program page](https://hackerone.com/anthropic). anthropic-sdk-python-0.120.2/api.md000066400000000000000000002213701523216435200170440ustar00rootroot00000000000000# Shared Types ```python from anthropic.types import ( APIErrorObject, AuthenticationError, BillingError, ErrorObject, ErrorResponse, ErrorType, GatewayTimeoutError, InvalidRequestError, NotFoundError, OverloadedError, PermissionError, RateLimitError, ) ``` # Messages Types: ```python from anthropic.types import ( Base64ImageSource, Base64PDFSource, BashCodeExecutionOutputBlock, BashCodeExecutionOutputBlockParam, BashCodeExecutionResultBlock, BashCodeExecutionResultBlockParam, BashCodeExecutionToolResultBlock, BashCodeExecutionToolResultBlockParam, BashCodeExecutionToolResultError, BashCodeExecutionToolResultErrorCode, BashCodeExecutionToolResultErrorParam, CacheControlEphemeral, CacheCreation, CitationCharLocation, CitationCharLocationParam, CitationContentBlockLocation, CitationContentBlockLocationParam, CitationPageLocation, CitationPageLocationParam, CitationSearchResultLocationParam, CitationWebSearchResultLocationParam, CitationsConfig, CitationsConfigParam, CitationsDelta, CitationsSearchResultLocation, CitationsWebSearchResultLocation, CodeExecutionOutputBlock, CodeExecutionOutputBlockParam, CodeExecutionResultBlock, CodeExecutionResultBlockParam, CodeExecutionTool20250522, CodeExecutionTool20250825, CodeExecutionTool20260120, CodeExecutionTool20260521, CodeExecutionToolResultBlock, CodeExecutionToolResultBlockContent, CodeExecutionToolResultBlockParam, CodeExecutionToolResultBlockParamContent, CodeExecutionToolResultError, CodeExecutionToolResultErrorCode, CodeExecutionToolResultErrorParam, Container, ContainerUploadBlock, ContainerUploadBlockParam, ContentBlock, ContentBlockParam, ContentBlockSource, ContentBlockSourceContent, DirectCaller, DocumentBlock, DocumentBlockParam, EncryptedCodeExecutionResultBlock, EncryptedCodeExecutionResultBlockParam, ImageBlockParam, InputJSONDelta, JSONOutputFormat, MemoryTool20250818, Message, MessageCountTokensTool, MessageDeltaUsage, MessageParam, MessageTokensCount, Metadata, MidConversationSystemBlockParam, Model, OutputConfig, OutputTokensDetails, PlainTextSource, RawContentBlockDelta, RawContentBlockDeltaEvent, RawContentBlockStartEvent, RawContentBlockStopEvent, RawMessageDeltaEvent, RawMessageStartEvent, RawMessageStopEvent, RawMessageStreamEvent, RedactedThinkingBlock, RedactedThinkingBlockParam, RefusalStopDetails, SearchResultBlockParam, ServerToolCaller, ServerToolCaller20260120, ServerToolUsage, ServerToolUseBlock, ServerToolUseBlockParam, SignatureDelta, StopReason, TextBlock, TextBlockParam, TextCitation, TextCitationParam, TextDelta, TextEditorCodeExecutionCreateResultBlock, TextEditorCodeExecutionCreateResultBlockParam, TextEditorCodeExecutionStrReplaceResultBlock, TextEditorCodeExecutionStrReplaceResultBlockParam, TextEditorCodeExecutionToolResultBlock, TextEditorCodeExecutionToolResultBlockParam, TextEditorCodeExecutionToolResultError, TextEditorCodeExecutionToolResultErrorCode, TextEditorCodeExecutionToolResultErrorParam, TextEditorCodeExecutionViewResultBlock, TextEditorCodeExecutionViewResultBlockParam, ThinkingBlock, ThinkingBlockParam, ThinkingConfigAdaptive, ThinkingConfigDisabled, ThinkingConfigEnabled, ThinkingConfigParam, ThinkingDelta, Tool, ToolBash20250124, ToolChoice, ToolChoiceAny, ToolChoiceAuto, ToolChoiceNone, ToolChoiceTool, ToolReferenceBlock, ToolReferenceBlockParam, ToolResultBlockParam, ToolSearchToolBm25_20251119, ToolSearchToolRegex20251119, ToolSearchToolResultBlock, ToolSearchToolResultBlockParam, ToolSearchToolResultError, ToolSearchToolResultErrorCode, ToolSearchToolResultErrorParam, ToolSearchToolSearchResultBlock, ToolSearchToolSearchResultBlockParam, ToolTextEditor20250124, ToolTextEditor20250429, ToolTextEditor20250728, ToolUnion, ToolUseBlock, ToolUseBlockParam, URLImageSource, URLPDFSource, Usage, UserLocation, WebFetchBlock, WebFetchBlockParam, WebFetchTool20250910, WebFetchTool20260209, WebFetchTool20260309, WebFetchTool20260318, WebFetchToolResultBlock, WebFetchToolResultBlockParam, WebFetchToolResultErrorBlock, WebFetchToolResultErrorBlockParam, WebFetchToolResultErrorCode, WebSearchResultBlock, WebSearchResultBlockParam, WebSearchTool20250305, WebSearchTool20260209, WebSearchTool20260318, WebSearchToolRequestError, WebSearchToolResultBlock, WebSearchToolResultBlockContent, WebSearchToolResultBlockParam, WebSearchToolResultBlockParamContent, WebSearchToolResultError, WebSearchToolResultErrorCode, MessageStreamEvent, MessageStartEvent, MessageDeltaEvent, MessageStopEvent, ContentBlockStartEvent, ContentBlockDeltaEvent, ContentBlockStopEvent, ) ``` Methods: - client.messages.create(\*\*params) -> Message - client.messages.stream(\*args) -> MessageStreamManager[MessageStream] | MessageStreamManager[MessageStreamT] - client.messages.count_tokens(\*\*params) -> MessageTokensCount ## Batches Types: ```python from anthropic.types.messages import ( DeletedMessageBatch, MessageBatch, MessageBatchCanceledResult, MessageBatchErroredResult, MessageBatchExpiredResult, MessageBatchIndividualResponse, MessageBatchRequestCounts, MessageBatchResult, MessageBatchSucceededResult, ) ``` Methods: - client.messages.batches.create(\*\*params) -> MessageBatch - client.messages.batches.retrieve(message_batch_id) -> MessageBatch - client.messages.batches.list(\*\*params) -> SyncPage[MessageBatch] - client.messages.batches.delete(message_batch_id) -> DeletedMessageBatch - client.messages.batches.cancel(message_batch_id) -> MessageBatch - client.messages.batches.results(message_batch_id) -> JSONLDecoder[MessageBatchIndividualResponse] # Models Types: ```python from anthropic.types import ( CapabilitySupport, ContextManagementCapability, EffortCapability, ModelCapabilities, ModelInfo, ThinkingCapability, ThinkingTypes, ) ``` Methods: - client.models.retrieve(model_id) -> ModelInfo - client.models.list(\*\*params) -> SyncPage[ModelInfo] # Beta Types: ```python from anthropic.types import ( AnthropicBeta, BetaAPIError, BetaAuthenticationError, BetaBillingError, BetaError, BetaErrorResponse, BetaGatewayTimeoutError, BetaInvalidRequestError, BetaNotFoundError, BetaOverloadedError, BetaPermissionError, BetaRateLimitError, ) ``` ## Models Types: ```python from anthropic.types.beta import ( BetaCapabilitySupport, BetaContextManagementCapability, BetaEffortCapability, BetaModelCapabilities, BetaModelInfo, BetaThinkingCapability, BetaThinkingTypes, ) ``` Methods: - client.beta.models.retrieve(model_id) -> BetaModelInfo - client.beta.models.list(\*\*params) -> SyncPage[BetaModelInfo] ## Messages Types: ```python from anthropic.types.beta import ( BetaAdvisorMessageIterationUsage, BetaAdvisorRedactedResultBlock, BetaAdvisorRedactedResultBlockParam, BetaAdvisorResultBlock, BetaAdvisorResultBlockParam, BetaAdvisorTool20260301, BetaAdvisorToolResultBlock, BetaAdvisorToolResultBlockParam, BetaAdvisorToolResultError, BetaAdvisorToolResultErrorParam, BetaAllThinkingTurns, BetaBase64ImageSource, BetaBase64PDFSource, BetaBashCodeExecutionOutputBlock, BetaBashCodeExecutionOutputBlockParam, BetaBashCodeExecutionResultBlock, BetaBashCodeExecutionResultBlockParam, BetaBashCodeExecutionToolResultBlock, BetaBashCodeExecutionToolResultBlockParam, BetaBashCodeExecutionToolResultError, BetaBashCodeExecutionToolResultErrorParam, BetaCacheControlEphemeral, BetaCacheCreation, BetaCacheMissMessagesChanged, BetaCacheMissModelChanged, BetaCacheMissPreviousMessageNotFound, BetaCacheMissSystemChanged, BetaCacheMissToolsChanged, BetaCacheMissUnavailable, BetaCitationCharLocation, BetaCitationCharLocationParam, BetaCitationConfig, BetaCitationContentBlockLocation, BetaCitationContentBlockLocationParam, BetaCitationPageLocation, BetaCitationPageLocationParam, BetaCitationSearchResultLocation, BetaCitationSearchResultLocationParam, BetaCitationWebSearchResultLocationParam, BetaCitationsConfigParam, BetaCitationsDelta, BetaCitationsWebSearchResultLocation, BetaClearThinking20251015Edit, BetaClearThinking20251015EditResponse, BetaClearToolUses20250919Edit, BetaClearToolUses20250919EditResponse, BetaCodeExecutionOutputBlock, BetaCodeExecutionOutputBlockParam, BetaCodeExecutionResultBlock, BetaCodeExecutionResultBlockParam, BetaCodeExecutionTool20250522, BetaCodeExecutionTool20250825, BetaCodeExecutionTool20260120, BetaCodeExecutionTool20260521, BetaCodeExecutionToolResultBlock, BetaCodeExecutionToolResultBlockContent, BetaCodeExecutionToolResultBlockParam, BetaCodeExecutionToolResultBlockParamContent, BetaCodeExecutionToolResultError, BetaCodeExecutionToolResultErrorCode, BetaCodeExecutionToolResultErrorParam, BetaCompact20260112Edit, BetaCompactionBlock, BetaCompactionBlockParam, BetaCompactionContentBlockDelta, BetaCompactionIterationUsage, BetaContainer, BetaContainerParams, BetaContainerUploadBlock, BetaContainerUploadBlockParam, BetaContentBlock, BetaContentBlockParam, BetaContentBlockSource, BetaContentBlockSourceContent, BetaContextManagementConfig, BetaContextManagementResponse, BetaCountTokensContextManagementResponse, BetaDiagnostics, BetaDiagnosticsParam, BetaDirectCaller, BetaDocumentBlock, BetaEncryptedCodeExecutionResultBlock, BetaEncryptedCodeExecutionResultBlockParam, BetaFallbackBlock, BetaFallbackBlockParam, BetaFallbackCreditNotApplied, BetaFallbackCreditRedeemed, BetaFallbackCreditTokenParam, BetaFallbackCreditUsage, BetaFallbackInfo, BetaFallbackInfoParam, BetaFallbackMessageIterationUsage, BetaFallbackParam, BetaFallbackRefusalTrigger, BetaFallbacksParam, BetaFileDocumentSource, BetaFileImageSource, BetaImageBlockParam, BetaInputJSONDelta, BetaInputTokensClearAtLeast, BetaInputTokensTrigger, BetaIterationsUsage, BetaJSONOutputFormat, BetaMCPToolConfig, BetaMCPToolDefaultConfig, BetaMCPToolResultBlock, BetaMCPToolUseBlock, BetaMCPToolUseBlockParam, BetaMCPToolset, BetaMemoryTool20250818, BetaMemoryTool20250818Command, BetaMemoryTool20250818CreateCommand, BetaMemoryTool20250818DeleteCommand, BetaMemoryTool20250818InsertCommand, BetaMemoryTool20250818RenameCommand, BetaMemoryTool20250818StrReplaceCommand, BetaMemoryTool20250818ViewCommand, BetaMessage, BetaMessageDeltaUsage, BetaMessageIterationUsage, BetaMessageParam, BetaMessageTokensCount, BetaMetadata, BetaMidConversationSystemBlockParam, BetaOutputConfig, BetaOutputTokensDetails, BetaPlainTextSource, BetaRawContentBlockDelta, BetaRawContentBlockDeltaEvent, BetaRawContentBlockStartEvent, BetaRawContentBlockStopEvent, BetaRawMessageDeltaEvent, BetaRawMessageStartEvent, BetaRawMessageStopEvent, BetaRawMessageStreamEvent, BetaRedactedThinkingBlock, BetaRedactedThinkingBlockParam, BetaRefusalStopDetails, BetaRequestDocumentBlock, BetaRequestMCPServerToolConfiguration, BetaRequestMCPServerURLDefinition, BetaRequestMCPToolResultBlockParam, BetaRequestToolAdditionBlock, BetaRequestToolRemovalBlock, BetaSearchResultBlockParam, BetaServerToolCaller, BetaServerToolCaller20260120, BetaServerToolUsage, BetaServerToolUseBlock, BetaServerToolUseBlockParam, BetaSignatureDelta, BetaSkill, BetaSkillParams, BetaStopReason, BetaTextBlock, BetaTextBlockParam, BetaTextCitation, BetaTextCitationParam, BetaTextDelta, BetaTextEditorCodeExecutionCreateResultBlock, BetaTextEditorCodeExecutionCreateResultBlockParam, BetaTextEditorCodeExecutionStrReplaceResultBlock, BetaTextEditorCodeExecutionStrReplaceResultBlockParam, BetaTextEditorCodeExecutionToolResultBlock, BetaTextEditorCodeExecutionToolResultBlockParam, BetaTextEditorCodeExecutionToolResultError, BetaTextEditorCodeExecutionToolResultErrorParam, BetaTextEditorCodeExecutionViewResultBlock, BetaTextEditorCodeExecutionViewResultBlockParam, BetaThinkingBlock, BetaThinkingBlockParam, BetaThinkingConfigAdaptive, BetaThinkingConfigDisabled, BetaThinkingConfigEnabled, BetaThinkingConfigParam, BetaThinkingDelta, BetaThinkingTurns, BetaTokenTaskBudget, BetaTool, BetaToolBash20241022, BetaToolBash20250124, BetaToolChangeMCPToolReference, BetaToolChangeMCPToolsetReference, BetaToolChangeToolReference, BetaToolChoice, BetaToolChoiceAny, BetaToolChoiceAuto, BetaToolChoiceNone, BetaToolChoiceTool, BetaToolComputerUse20241022, BetaToolComputerUse20250124, BetaToolComputerUse20251124, BetaToolReferenceBlock, BetaToolReferenceBlockParam, BetaToolResultBlockParam, BetaToolSearchToolBm25_20251119, BetaToolSearchToolRegex20251119, BetaToolSearchToolResultBlock, BetaToolSearchToolResultBlockParam, BetaToolSearchToolResultError, BetaToolSearchToolResultErrorParam, BetaToolSearchToolSearchResultBlock, BetaToolSearchToolSearchResultBlockParam, BetaToolTextEditor20241022, BetaToolTextEditor20250124, BetaToolTextEditor20250429, BetaToolTextEditor20250728, BetaToolUnion, BetaToolUseBlock, BetaToolUseBlockParam, BetaToolUsesKeep, BetaToolUsesTrigger, BetaURLImageSource, BetaURLPDFSource, BetaUsage, BetaUserLocation, BetaWebFetchBlock, BetaWebFetchBlockParam, BetaWebFetchTool20250910, BetaWebFetchTool20260209, BetaWebFetchTool20260309, BetaWebFetchTool20260318, BetaWebFetchToolResultBlock, BetaWebFetchToolResultBlockParam, BetaWebFetchToolResultErrorBlock, BetaWebFetchToolResultErrorBlockParam, BetaWebFetchToolResultErrorCode, BetaWebSearchResultBlock, BetaWebSearchResultBlockParam, BetaWebSearchTool20250305, BetaWebSearchTool20260209, BetaWebSearchTool20260318, BetaWebSearchToolRequestError, BetaWebSearchToolResultBlock, BetaWebSearchToolResultBlockContent, BetaWebSearchToolResultBlockParam, BetaWebSearchToolResultBlockParamContent, BetaWebSearchToolResultError, BetaWebSearchToolResultErrorCode, BetaBase64PDFBlock, ) ``` Methods: - client.beta.messages.create(\*\*params) -> BetaMessage - client.beta.messages.count_tokens(\*\*params) -> BetaMessageTokensCount ### Batches Types: ```python from anthropic.types.beta.messages import ( BetaDeletedMessageBatch, BetaMessageBatch, BetaMessageBatchCanceledResult, BetaMessageBatchErroredResult, BetaMessageBatchExpiredResult, BetaMessageBatchIndividualResponse, BetaMessageBatchRequestCounts, BetaMessageBatchResult, BetaMessageBatchSucceededResult, ) ``` Methods: - client.beta.messages.batches.create(\*\*params) -> BetaMessageBatch - client.beta.messages.batches.retrieve(message_batch_id) -> BetaMessageBatch - client.beta.messages.batches.list(\*\*params) -> SyncPage[BetaMessageBatch] - client.beta.messages.batches.delete(message_batch_id) -> BetaDeletedMessageBatch - client.beta.messages.batches.cancel(message_batch_id) -> BetaMessageBatch - client.beta.messages.batches.results(message_batch_id) -> JSONLDecoder[BetaMessageBatchIndividualResponse] ## Agents Types: ```python from anthropic.types.beta import ( BetaManagedAgentsAgent, BetaManagedAgentsAgentReference, BetaManagedAgentsAgentToolConfig, BetaManagedAgentsAgentToolConfigParams, BetaManagedAgentsAgentToolsetDefaultConfig, BetaManagedAgentsAgentToolsetDefaultConfigParams, BetaManagedAgentsAgentToolset20260401, BetaManagedAgentsAgentToolset20260401BashInput, BetaManagedAgentsAgentToolset20260401EditInput, BetaManagedAgentsAgentToolset20260401GlobInput, BetaManagedAgentsAgentToolset20260401GrepInput, BetaManagedAgentsAgentToolset20260401Params, BetaManagedAgentsAgentToolset20260401ReadInput, BetaManagedAgentsAgentToolset20260401WriteInput, BetaManagedAgentsAlwaysAllowPolicy, BetaManagedAgentsAlwaysAskPolicy, BetaManagedAgentsAnthropicSkill, BetaManagedAgentsAnthropicSkillParams, BetaManagedAgentsCustomSkill, BetaManagedAgentsCustomSkillParams, BetaManagedAgentsCustomTool, BetaManagedAgentsCustomToolInputSchema, BetaManagedAgentsCustomToolParams, BetaManagedAgentsEffortHigh, BetaManagedAgentsEffortLow, BetaManagedAgentsEffortMax, BetaManagedAgentsEffortMedium, BetaManagedAgentsEffortXhigh, BetaManagedAgentsMCPServerURLDefinition, BetaManagedAgentsMCPToolConfig, BetaManagedAgentsMCPToolConfigParams, BetaManagedAgentsMCPToolset, BetaManagedAgentsMCPToolsetDefaultConfig, BetaManagedAgentsMCPToolsetDefaultConfigParams, BetaManagedAgentsMCPToolsetParams, BetaManagedAgentsModel, BetaManagedAgentsModelConfig, BetaManagedAgentsModelConfigParams, BetaManagedAgentsMultiagentCoordinator, BetaManagedAgentsMultiagentCoordinatorParams, BetaManagedAgentsMultiagentSelfParams, BetaManagedAgentsSessionThreadAgent, BetaManagedAgentsSkillParams, BetaManagedAgentsURLMCPServerParams, ) ``` Methods: - client.beta.agents.create(\*\*params) -> BetaManagedAgentsAgent - client.beta.agents.retrieve(agent_id, \*\*params) -> BetaManagedAgentsAgent - client.beta.agents.update(agent_id, \*\*params) -> BetaManagedAgentsAgent - client.beta.agents.list(\*\*params) -> SyncPageCursor[BetaManagedAgentsAgent] - client.beta.agents.archive(agent_id) -> BetaManagedAgentsAgent ### Versions Methods: - client.beta.agents.versions.list(agent_id, \*\*params) -> SyncPageCursor[BetaManagedAgentsAgent] ## Environments Types: ```python from anthropic.types.beta import ( BetaCloudConfig, BetaCloudConfigParams, BetaEnvironment, BetaEnvironmentDeleteResponse, BetaLimitedNetwork, BetaLimitedNetworkParams, BetaPackages, BetaPackagesParams, BetaSelfHostedConfig, BetaSelfHostedConfigParams, BetaUnrestrictedNetwork, ) ``` Methods: - client.beta.environments.create(\*\*params) -> BetaEnvironment - client.beta.environments.retrieve(environment_id) -> BetaEnvironment - client.beta.environments.update(environment_id, \*\*params) -> BetaEnvironment - client.beta.environments.list(\*\*params) -> SyncPageCursor[BetaEnvironment] - client.beta.environments.delete(environment_id) -> BetaEnvironmentDeleteResponse - client.beta.environments.archive(environment_id) -> BetaEnvironment ### Work Types: ```python from anthropic.types.beta.environments import ( BetaSelfHostedWork, BetaSelfHostedWorkHeartbeatResponse, BetaSelfHostedWorkListResponse, BetaSelfHostedWorkQueueStats, BetaSelfHostedWorkStopRequest, BetaSelfHostedWorkUpdateRequest, BetaSessionWorkData, ) ``` Methods: - client.beta.environments.work.retrieve(work_id, \*, environment_id) -> BetaSelfHostedWork - client.beta.environments.work.update(work_id, \*, environment_id, \*\*params) -> BetaSelfHostedWork - client.beta.environments.work.list(environment_id, \*\*params) -> SyncPageCursor[BetaSelfHostedWork] - client.beta.environments.work.ack(work_id, \*, environment_id) -> BetaSelfHostedWork - client.beta.environments.work.heartbeat(work_id, \*, environment_id, \*\*params) -> BetaSelfHostedWorkHeartbeatResponse - client.beta.environments.work.poll(environment_id, \*\*params) -> Optional[BetaSelfHostedWork] - client.beta.environments.work.stats(environment_id) -> BetaSelfHostedWorkQueueStats - client.beta.environments.work.stop(work_id, \*, environment_id, \*\*params) -> BetaSelfHostedWork ## Sessions Types: ```python from anthropic.types.beta import ( BetaManagedAgentsAgentMessagePreview, BetaManagedAgentsAgentParams, BetaManagedAgentsAgentThinkingPreview, BetaManagedAgentsAgentWithOverridesParams, BetaManagedAgentsBranchCheckout, BetaManagedAgentsCacheCreationUsage, BetaManagedAgentsCommitCheckout, BetaManagedAgentsDeletedSession, BetaManagedAgentsDeltaContent, BetaManagedAgentsDeltaEvent, BetaManagedAgentsDeltaType, BetaManagedAgentsFileResourceParams, BetaManagedAgentsGitHubRepositoryResourceParams, BetaManagedAgentsMemoryStoreResourceParam, BetaManagedAgentsMultiagent, BetaManagedAgentsMultiagentParams, BetaManagedAgentsMultiagentRosterEntryParams, BetaManagedAgentsOutcomeEvaluationResource, BetaManagedAgentsSession, BetaManagedAgentsSessionAgent, BetaManagedAgentsSessionAgentUpdate, BetaManagedAgentsSessionMultiagentCoordinator, BetaManagedAgentsSessionStats, BetaManagedAgentsSessionUpdatedEvent, BetaManagedAgentsSessionUsage, BetaManagedAgentsStartEvent, BetaManagedAgentsStartEventPreview, BetaManagedAgentsSystemContentBlock, BetaManagedAgentsSystemMessageEvent, BetaManagedAgentsUserToolResultEvent, ) ``` Methods: - client.beta.sessions.create(\*\*params) -> BetaManagedAgentsSession - client.beta.sessions.retrieve(session_id) -> BetaManagedAgentsSession - client.beta.sessions.update(session_id, \*\*params) -> BetaManagedAgentsSession - client.beta.sessions.list(\*\*params) -> SyncBidirectionalPageCursor[BetaManagedAgentsSession] - client.beta.sessions.delete(session_id) -> BetaManagedAgentsDeletedSession - client.beta.sessions.archive(session_id) -> BetaManagedAgentsSession ### Events Types: ```python from anthropic.types.beta.sessions import ( BetaManagedAgentsAgentCustomToolUseEvent, BetaManagedAgentsAgentMCPToolResultEvent, BetaManagedAgentsAgentMCPToolUseEvent, BetaManagedAgentsAgentMessageEvent, BetaManagedAgentsAgentThinkingEvent, BetaManagedAgentsAgentThreadContextCompactedEvent, BetaManagedAgentsAgentThreadMessageReceivedEvent, BetaManagedAgentsAgentThreadMessageSentEvent, BetaManagedAgentsAgentToolResultEvent, BetaManagedAgentsAgentToolUseEvent, BetaManagedAgentsBase64DocumentSource, BetaManagedAgentsBase64ImageSource, BetaManagedAgentsBillingError, BetaManagedAgentsCredentialHostUnreachableError, BetaManagedAgentsDocumentBlock, BetaManagedAgentsEventParams, BetaManagedAgentsFileDocumentSource, BetaManagedAgentsFileImageSource, BetaManagedAgentsFileRubric, BetaManagedAgentsFileRubricParams, BetaManagedAgentsImageBlock, BetaManagedAgentsMCPAuthenticationFailedError, BetaManagedAgentsMCPConnectionFailedError, BetaManagedAgentsModelOverloadedError, BetaManagedAgentsModelRateLimitedError, BetaManagedAgentsModelRequestFailedError, BetaManagedAgentsPlainTextDocumentSource, BetaManagedAgentsRetryStatusExhausted, BetaManagedAgentsRetryStatusRetrying, BetaManagedAgentsRetryStatusTerminal, BetaManagedAgentsSearchResultBlock, BetaManagedAgentsSearchResultCitations, BetaManagedAgentsSearchResultContent, BetaManagedAgentsSendSessionEvents, BetaManagedAgentsSessionDeletedEvent, BetaManagedAgentsSessionEndTurn, BetaManagedAgentsSessionErrorEvent, BetaManagedAgentsSessionEvent, BetaManagedAgentsSessionRequiresAction, BetaManagedAgentsSessionRetriesExhausted, BetaManagedAgentsSessionStatusIdleEvent, BetaManagedAgentsSessionStatusRescheduledEvent, BetaManagedAgentsSessionStatusRunningEvent, BetaManagedAgentsSessionStatusTerminatedEvent, BetaManagedAgentsSessionThreadCreatedEvent, BetaManagedAgentsSessionThreadStatusIdleEvent, BetaManagedAgentsSessionThreadStatusRescheduledEvent, BetaManagedAgentsSessionThreadStatusRunningEvent, BetaManagedAgentsSessionThreadStatusTerminatedEvent, BetaManagedAgentsSpanModelRequestEndEvent, BetaManagedAgentsSpanModelRequestStartEvent, BetaManagedAgentsSpanModelUsage, BetaManagedAgentsSpanOutcomeEvaluationEndEvent, BetaManagedAgentsSpanOutcomeEvaluationOngoingEvent, BetaManagedAgentsSpanOutcomeEvaluationStartEvent, BetaManagedAgentsStreamSessionEvents, BetaManagedAgentsSystemMessageEventParams, BetaManagedAgentsTextBlock, BetaManagedAgentsTextRubric, BetaManagedAgentsTextRubricParams, BetaManagedAgentsUnknownError, BetaManagedAgentsURLDocumentSource, BetaManagedAgentsURLImageSource, BetaManagedAgentsUserCustomToolResultEvent, BetaManagedAgentsUserCustomToolResultEventParams, BetaManagedAgentsUserDefineOutcomeEvent, BetaManagedAgentsUserDefineOutcomeEventParams, BetaManagedAgentsUserInterruptEvent, BetaManagedAgentsUserInterruptEventParams, BetaManagedAgentsUserMessageEvent, BetaManagedAgentsUserMessageEventParams, BetaManagedAgentsUserToolConfirmationEvent, BetaManagedAgentsUserToolConfirmationEventParams, BetaManagedAgentsUserToolResultEventParams, ) ``` Methods: - client.beta.sessions.events.list(session_id, \*\*params) -> SyncPageCursor[BetaManagedAgentsSessionEvent] - client.beta.sessions.events.send(session_id, \*\*params) -> BetaManagedAgentsSendSessionEvents - client.beta.sessions.events.stream(session_id, \*\*params) -> BetaManagedAgentsStreamSessionEvents ### Resources Types: ```python from anthropic.types.beta.sessions import ( BetaManagedAgentsDeleteSessionResource, BetaManagedAgentsFileResource, BetaManagedAgentsGitHubRepositoryResource, BetaManagedAgentsMemoryStoreResource, BetaManagedAgentsSessionResource, ResourceRetrieveResponse, ResourceUpdateResponse, ) ``` Methods: - client.beta.sessions.resources.retrieve(resource_id, \*, session_id) -> ResourceRetrieveResponse - client.beta.sessions.resources.update(resource_id, \*, session_id, \*\*params) -> ResourceUpdateResponse - client.beta.sessions.resources.list(session_id, \*\*params) -> SyncPageCursor[BetaManagedAgentsSessionResource] - client.beta.sessions.resources.delete(resource_id, \*, session_id) -> BetaManagedAgentsDeleteSessionResource - client.beta.sessions.resources.add(session_id, \*\*params) -> BetaManagedAgentsFileResource ### Threads Types: ```python from anthropic.types.beta.sessions import ( BetaManagedAgentsSessionThread, BetaManagedAgentsSessionThreadStats, BetaManagedAgentsSessionThreadStatus, BetaManagedAgentsSessionThreadUsage, BetaManagedAgentsStreamSessionThreadEvents, ) ``` Methods: - client.beta.sessions.threads.retrieve(thread_id, \*, session_id) -> BetaManagedAgentsSessionThread - client.beta.sessions.threads.list(session_id, \*\*params) -> SyncPageCursor[BetaManagedAgentsSessionThread] - client.beta.sessions.threads.archive(thread_id, \*, session_id) -> BetaManagedAgentsSessionThread #### Events Methods: - client.beta.sessions.threads.events.list(thread_id, \*, session_id, \*\*params) -> SyncPageCursor[BetaManagedAgentsSessionEvent] - client.beta.sessions.threads.events.stream(thread_id, \*, session_id, \*\*params) -> BetaManagedAgentsStreamSessionThreadEvents ## Deployments Types: ```python from anthropic.types.beta import ( BetaManagedAgentsAgentArchivedDeploymentPausedReasonError, BetaManagedAgentsCronSchedule, BetaManagedAgentsCronScheduleParams, BetaManagedAgentsDeployment, BetaManagedAgentsDeploymentInitialEvent, BetaManagedAgentsDeploymentInitialEventParams, BetaManagedAgentsDeploymentPausedReason, BetaManagedAgentsDeploymentPausedReasonError, BetaManagedAgentsDeploymentStatus, BetaManagedAgentsDeploymentSystemMessageEvent, BetaManagedAgentsDeploymentUserDefineOutcomeEvent, BetaManagedAgentsDeploymentUserMessageEvent, BetaManagedAgentsEnvironmentArchivedDeploymentPausedReasonError, BetaManagedAgentsEnvironmentNotFoundDeploymentPausedReasonError, BetaManagedAgentsErrorDeploymentPausedReason, BetaManagedAgentsFileNotFoundDeploymentPausedReasonError, BetaManagedAgentsFileResourceConfig, BetaManagedAgentsGitHubRepositoryResourceConfig, BetaManagedAgentsManualDeploymentPausedReason, BetaManagedAgentsMCPEgressBlockedDeploymentPausedReasonError, BetaManagedAgentsMemoryStoreArchivedDeploymentPausedReasonError, BetaManagedAgentsMemoryStoreResourceConfig, BetaManagedAgentsOrganizationDisabledDeploymentPausedReasonError, BetaManagedAgentsSchedule, BetaManagedAgentsScheduleParams, BetaManagedAgentsSelfHostedResourcesUnsupportedDeploymentPausedReasonError, BetaManagedAgentsSessionResourceConfig, BetaManagedAgentsSessionResourceNotFoundDeploymentPausedReasonError, BetaManagedAgentsSkillNotFoundDeploymentPausedReasonError, BetaManagedAgentsUnknownDeploymentPausedReasonError, BetaManagedAgentsVaultArchivedDeploymentPausedReasonError, BetaManagedAgentsVaultNotFoundDeploymentPausedReasonError, BetaManagedAgentsWorkspaceArchivedDeploymentPausedReasonError, ) ``` Methods: - client.beta.deployments.create(\*\*params) -> BetaManagedAgentsDeployment - client.beta.deployments.retrieve(deployment_id) -> BetaManagedAgentsDeployment - client.beta.deployments.update(deployment_id, \*\*params) -> BetaManagedAgentsDeployment - client.beta.deployments.list(\*\*params) -> SyncPageCursor[BetaManagedAgentsDeployment] - client.beta.deployments.archive(deployment_id) -> BetaManagedAgentsDeployment - client.beta.deployments.pause(deployment_id) -> BetaManagedAgentsDeployment - client.beta.deployments.run(deployment_id) -> BetaManagedAgentsDeploymentRun - client.beta.deployments.unpause(deployment_id) -> BetaManagedAgentsDeployment ## DeploymentRuns Types: ```python from anthropic.types.beta import ( BetaManagedAgentsAgentArchivedRunError, BetaManagedAgentsDeploymentRun, BetaManagedAgentsEnvironmentArchivedRunError, BetaManagedAgentsEnvironmentNotFoundRunError, BetaManagedAgentsFileNotFoundRunError, BetaManagedAgentsManualTriggerContext, BetaManagedAgentsMCPEgressBlockedRunError, BetaManagedAgentsMemoryStoreArchivedRunError, BetaManagedAgentsOrganizationDisabledRunError, BetaManagedAgentsScheduleTriggerContext, BetaManagedAgentsSelfHostedResourcesUnsupportedRunError, BetaManagedAgentsSessionCreationRejectedRunError, BetaManagedAgentsSessionRateLimitedRunError, BetaManagedAgentsSessionResourceNotFoundRunError, BetaManagedAgentsSkillNotFoundRunError, BetaManagedAgentsTriggerContext, BetaManagedAgentsTriggerType, BetaManagedAgentsUnknownRunError, BetaManagedAgentsVaultArchivedRunError, BetaManagedAgentsVaultNotFoundRunError, BetaManagedAgentsWorkspaceArchivedRunError, ) ``` Methods: - client.beta.deployment_runs.retrieve(deployment_run_id) -> BetaManagedAgentsDeploymentRun - client.beta.deployment_runs.list(\*\*params) -> SyncPageCursor[BetaManagedAgentsDeploymentRun] ## Vaults Types: ```python from anthropic.types.beta import BetaManagedAgentsDeletedVault, BetaManagedAgentsVault ``` Methods: - client.beta.vaults.create(\*\*params) -> BetaManagedAgentsVault - client.beta.vaults.retrieve(vault_id) -> BetaManagedAgentsVault - client.beta.vaults.update(vault_id, \*\*params) -> BetaManagedAgentsVault - client.beta.vaults.list(\*\*params) -> SyncPageCursor[BetaManagedAgentsVault] - client.beta.vaults.delete(vault_id) -> BetaManagedAgentsDeletedVault - client.beta.vaults.archive(vault_id) -> BetaManagedAgentsVault ### Credentials Types: ```python from anthropic.types.beta.vaults import ( BetaManagedAgentsCredential, BetaManagedAgentsCredentialNetworkingParams, BetaManagedAgentsCredentialValidation, BetaManagedAgentsCredentialValidationStatus, BetaManagedAgentsDeletedCredential, BetaManagedAgentsEnvironmentVariableAuthResponse, BetaManagedAgentsEnvironmentVariableCreateParams, BetaManagedAgentsEnvironmentVariableUpdateParams, BetaManagedAgentsInjectionLocationParams, BetaManagedAgentsInjectionLocationResponse, BetaManagedAgentsInjectionLocationUpdateParams, BetaManagedAgentsLimitedCredentialNetworkingParams, BetaManagedAgentsLimitedCredentialNetworkingResponse, BetaManagedAgentsMCPOAuthAuthResponse, BetaManagedAgentsMCPOAuthCreateParams, BetaManagedAgentsMCPOAuthRefreshParams, BetaManagedAgentsMCPOAuthRefreshResponse, BetaManagedAgentsMCPOAuthRefreshUpdateParams, BetaManagedAgentsMCPOAuthUpdateParams, BetaManagedAgentsMCPProbe, BetaManagedAgentsRefreshHTTPResponse, BetaManagedAgentsRefreshObject, BetaManagedAgentsStaticBearerAuthResponse, BetaManagedAgentsStaticBearerCreateParams, BetaManagedAgentsStaticBearerUpdateParams, BetaManagedAgentsTokenEndpointAuthBasicParam, BetaManagedAgentsTokenEndpointAuthBasicResponse, BetaManagedAgentsTokenEndpointAuthBasicUpdateParam, BetaManagedAgentsTokenEndpointAuthNoneParam, BetaManagedAgentsTokenEndpointAuthNoneResponse, BetaManagedAgentsTokenEndpointAuthPostParam, BetaManagedAgentsTokenEndpointAuthPostResponse, BetaManagedAgentsTokenEndpointAuthPostUpdateParam, BetaManagedAgentsUnrestrictedCredentialNetworkingParams, BetaManagedAgentsUnrestrictedCredentialNetworkingResponse, ) ``` Methods: - client.beta.vaults.credentials.create(vault_id, \*\*params) -> BetaManagedAgentsCredential - client.beta.vaults.credentials.retrieve(credential_id, \*, vault_id) -> BetaManagedAgentsCredential - client.beta.vaults.credentials.update(credential_id, \*, vault_id, \*\*params) -> BetaManagedAgentsCredential - client.beta.vaults.credentials.list(vault_id, \*\*params) -> SyncPageCursor[BetaManagedAgentsCredential] - client.beta.vaults.credentials.delete(credential_id, \*, vault_id) -> BetaManagedAgentsDeletedCredential - client.beta.vaults.credentials.archive(credential_id, \*, vault_id) -> BetaManagedAgentsCredential - client.beta.vaults.credentials.mcp_oauth_validate(credential_id, \*, vault_id) -> BetaManagedAgentsCredentialValidation ## MemoryStores Types: ```python from anthropic.types.beta import BetaManagedAgentsDeletedMemoryStore, BetaManagedAgentsMemoryStore ``` Methods: - client.beta.memory_stores.create(\*\*params) -> BetaManagedAgentsMemoryStore - client.beta.memory_stores.retrieve(memory_store_id) -> BetaManagedAgentsMemoryStore - client.beta.memory_stores.update(memory_store_id, \*\*params) -> BetaManagedAgentsMemoryStore - client.beta.memory_stores.list(\*\*params) -> SyncPageCursor[BetaManagedAgentsMemoryStore] - client.beta.memory_stores.delete(memory_store_id) -> BetaManagedAgentsDeletedMemoryStore - client.beta.memory_stores.archive(memory_store_id) -> BetaManagedAgentsMemoryStore ### Memories Types: ```python from anthropic.types.beta.memory_stores import ( BetaManagedAgentsConflictError, BetaManagedAgentsContentSha256Precondition, BetaManagedAgentsDeletedMemory, BetaManagedAgentsError, BetaManagedAgentsMemory, BetaManagedAgentsMemoryListItem, BetaManagedAgentsMemoryPathConflictError, BetaManagedAgentsMemoryPreconditionFailedError, BetaManagedAgentsMemoryPrefix, BetaManagedAgentsMemoryView, BetaManagedAgentsPrecondition, ) ``` Methods: - client.beta.memory_stores.memories.create(memory_store_id, \*\*params) -> BetaManagedAgentsMemory - client.beta.memory_stores.memories.retrieve(memory_id, \*, memory_store_id, \*\*params) -> BetaManagedAgentsMemory - client.beta.memory_stores.memories.update(memory_id, \*, memory_store_id, \*\*params) -> BetaManagedAgentsMemory - client.beta.memory_stores.memories.list(memory_store_id, \*\*params) -> SyncPageCursor[BetaManagedAgentsMemoryListItem] - client.beta.memory_stores.memories.delete(memory_id, \*, memory_store_id, \*\*params) -> BetaManagedAgentsDeletedMemory ### MemoryVersions Types: ```python from anthropic.types.beta.memory_stores import ( BetaManagedAgentsActor, BetaManagedAgentsAPIActor, BetaManagedAgentsMemoryVersion, BetaManagedAgentsMemoryVersionOperation, BetaManagedAgentsSessionActor, BetaManagedAgentsUserActor, ) ``` Methods: - client.beta.memory_stores.memory_versions.retrieve(memory_version_id, \*, memory_store_id, \*\*params) -> BetaManagedAgentsMemoryVersion - client.beta.memory_stores.memory_versions.list(memory_store_id, \*\*params) -> SyncPageCursor[BetaManagedAgentsMemoryVersion] - client.beta.memory_stores.memory_versions.redact(memory_version_id, \*, memory_store_id) -> BetaManagedAgentsMemoryVersion ## Files Types: ```python from anthropic.types.beta import BetaFileScope, DeletedFile, FileMetadata ``` Methods: - client.beta.files.list(\*\*params) -> SyncPage[FileMetadata] - client.beta.files.delete(file_id) -> DeletedFile - client.beta.files.download(file_id) -> BinaryAPIResponse - client.beta.files.retrieve_metadata(file_id) -> FileMetadata - client.beta.files.upload(\*\*params) -> FileMetadata ## Skills Types: ```python from anthropic.types.beta import ( SkillCreateResponse, SkillRetrieveResponse, SkillListResponse, SkillDeleteResponse, ) ``` Methods: - client.beta.skills.create(\*\*params) -> SkillCreateResponse - client.beta.skills.retrieve(skill_id) -> SkillRetrieveResponse - client.beta.skills.list(\*\*params) -> SyncPageCursor[SkillListResponse] - client.beta.skills.delete(skill_id) -> SkillDeleteResponse ### Versions Types: ```python from anthropic.types.beta.skills import ( VersionCreateResponse, VersionRetrieveResponse, VersionListResponse, VersionDeleteResponse, ) ``` Methods: - client.beta.skills.versions.create(skill_id, \*\*params) -> VersionCreateResponse - client.beta.skills.versions.retrieve(version, \*, skill_id) -> VersionRetrieveResponse - client.beta.skills.versions.list(skill_id, \*\*params) -> SyncPageCursor[VersionListResponse] - client.beta.skills.versions.delete(version, \*, skill_id) -> VersionDeleteResponse - client.beta.skills.versions.download(version, \*, skill_id) -> BinaryAPIResponse ## Webhooks Types: ```python from anthropic.types.beta import ( BetaWebhookAgentArchivedEventData, BetaWebhookAgentCreatedEventData, BetaWebhookAgentDeletedEventData, BetaWebhookAgentUpdatedEventData, BetaWebhookDeploymentArchivedEventData, BetaWebhookDeploymentCreatedEventData, BetaWebhookDeploymentDeletedEventData, BetaWebhookDeploymentPausedEventData, BetaWebhookDeploymentRunFailedEventData, BetaWebhookDeploymentRunStartedEventData, BetaWebhookDeploymentRunSucceededEventData, BetaWebhookDeploymentUnpausedEventData, BetaWebhookDeploymentUpdatedEventData, BetaWebhookEnvironmentArchivedEventData, BetaWebhookEnvironmentCreatedEventData, BetaWebhookEnvironmentDeletedEventData, BetaWebhookEnvironmentUpdatedEventData, BetaWebhookEvent, BetaWebhookEventData, BetaWebhookMemoryStoreArchivedEventData, BetaWebhookMemoryStoreCreatedEventData, BetaWebhookMemoryStoreDeletedEventData, BetaWebhookSessionArchivedEventData, BetaWebhookSessionCreatedEventData, BetaWebhookSessionDeletedEventData, BetaWebhookSessionIdledEventData, BetaWebhookSessionOutcomeEvaluationEndedEventData, BetaWebhookSessionPendingEventData, BetaWebhookSessionRequiresActionEventData, BetaWebhookSessionRunningEventData, BetaWebhookSessionStatusIdledEventData, BetaWebhookSessionStatusRescheduledEventData, BetaWebhookSessionStatusRunStartedEventData, BetaWebhookSessionStatusTerminatedEventData, BetaWebhookSessionThreadCreatedEventData, BetaWebhookSessionThreadIdledEventData, BetaWebhookSessionThreadTerminatedEventData, BetaWebhookSessionUpdatedEventData, BetaWebhookVaultArchivedEventData, BetaWebhookVaultCreatedEventData, BetaWebhookVaultCredentialArchivedEventData, BetaWebhookVaultCredentialCreatedEventData, BetaWebhookVaultCredentialDeletedEventData, BetaWebhookVaultCredentialRefreshFailedEventData, BetaWebhookVaultDeletedEventData, UnwrapWebhookEvent, ) ``` ## UserProfiles Types: ```python from anthropic.types.beta import ( BetaUserProfile, BetaUserProfileEnrollmentURL, BetaUserProfileTrustGrant, ) ``` Methods: - client.beta.user_profiles.create(\*\*params) -> BetaUserProfile - client.beta.user_profiles.retrieve(user_profile_id) -> BetaUserProfile - client.beta.user_profiles.update(user_profile_id, \*\*params) -> BetaUserProfile - client.beta.user_profiles.list(\*\*params) -> SyncPageCursor[BetaUserProfile] - client.beta.user_profiles.create_enrollment_url(user_profile_id) -> BetaUserProfileEnrollmentURL ## Dreams Types: ```python from anthropic.types.beta import ( BetaDream, BetaDreamError, BetaDreamInput, BetaDreamMemoryStoreInput, BetaDreamMemoryStoreOutput, BetaDreamModelConfig, BetaDreamModelConfigParam, BetaDreamOutput, BetaDreamSessionsInput, BetaDreamStatus, BetaDreamUsage, ) ``` Methods: - client.beta.dreams.create(\*\*params) -> BetaDream - client.beta.dreams.retrieve(dream_id) -> BetaDream - client.beta.dreams.list(\*\*params) -> SyncPageCursor[BetaDream] - client.beta.dreams.archive(dream_id) -> BetaDream - client.beta.dreams.cancel(dream_id) -> BetaDream ## Tunnels Types: ```python from anthropic.types.beta import BetaTunnel, BetaTunnelToken ``` Methods: - client.beta.tunnels.create(\*\*params) -> BetaTunnel - client.beta.tunnels.retrieve(tunnel_id) -> BetaTunnel - client.beta.tunnels.list(\*\*params) -> SyncPageCursor[BetaTunnel] - client.beta.tunnels.archive(tunnel_id) -> BetaTunnel - client.beta.tunnels.reveal_token(tunnel_id) -> BetaTunnelToken - client.beta.tunnels.rotate_token(tunnel_id, \*\*params) -> BetaTunnelToken ### Certificates Types: ```python from anthropic.types.beta.tunnels import BetaTunnelCertificate ``` Methods: - client.beta.tunnels.certificates.create(tunnel_id, \*\*params) -> BetaTunnelCertificate - client.beta.tunnels.certificates.retrieve(certificate_id, \*, tunnel_id) -> BetaTunnelCertificate - client.beta.tunnels.certificates.list(tunnel_id, \*\*params) -> SyncPageCursor[BetaTunnelCertificate] - client.beta.tunnels.certificates.archive(certificate_id, \*, tunnel_id) -> BetaTunnelCertificate anthropic-sdk-python-0.120.2/bin/000077500000000000000000000000001523216435200165145ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/bin/publish-pypi000066400000000000000000000002311523216435200210600ustar00rootroot00000000000000#!/usr/bin/env bash set -eux rm -rf dist mkdir -p dist uv build if [ -n "${PYPI_TOKEN:-}" ]; then uv publish --token=$PYPI_TOKEN else uv publish fi anthropic-sdk-python-0.120.2/examples/000077500000000000000000000000001523216435200175625ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/examples/.keep000066400000000000000000000003571523216435200205140ustar00rootroot00000000000000File generated from our OpenAPI spec by Stainless. This directory can be used to store example files demonstrating usage of this SDK. It is ignored by Stainless code generation and its content (other than this keep file) won't be touched.anthropic-sdk-python-0.120.2/examples/agents.py000077500000000000000000000025101523216435200214160ustar00rootroot00000000000000#!/usr/bin/env -S uv run python import os from anthropic import Anthropic def main() -> None: anthropic = Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY")) # Create an environment environment = anthropic.beta.environments.create( name="simple-example-environment", ) print("Created environment:", environment.id) # Create an agent agent = anthropic.beta.agents.create( name="simple-example-agent", model="claude-sonnet-5", ) print("Created agent:", agent.id) # Create a session session = anthropic.beta.sessions.create( environment_id=environment.id, agent={"type": "agent", "id": agent.id, "version": agent.version}, ) print("Created session:", session.id) # Send a prompt and stream events until the session goes idle print("Streaming events:") anthropic.beta.sessions.events.send( session.id, events=[ { "type": "user.message", "content": [{"type": "text", "text": "Hello Claude!"}], } ], ) with anthropic.beta.sessions.events.stream(session.id) as stream: for event in stream: print(event.to_json(indent=2)) if event.type == "session.status_idle": break if __name__ == "__main__": main() anthropic-sdk-python-0.120.2/examples/agents_comprehensive.py000066400000000000000000000110611523216435200243430ustar00rootroot00000000000000#!/usr/bin/env -S uv run python import os import time from anthropic import Anthropic MCP_SERVER_NAME = "github" MCP_SERVER_URL = "https://api.githubcopilot.com/mcp/" PROMPT = ( "Hi! List every tool and skill you have access to, grouped by where they " "came from (built-in toolset, custom tool, MCP server, skills)." ) def main() -> None: anthropic = Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY")) github_token = os.environ.get("GITHUB_TOKEN") if not github_token: raise RuntimeError("GITHUB_TOKEN is required (use a fine-grained PAT with public-repo read only)") # Create an environment environment = anthropic.beta.environments.create( name="comprehensive-example-environment", ) print("Created environment:", environment.id) # Create a vault and store the MCP server credential in it vault = anthropic.beta.vaults.create(display_name="comprehensive-example-vault") print("Created vault:", vault.id) credential = anthropic.beta.vaults.credentials.create( vault.id, display_name="github-mcp", auth={ "type": "static_bearer", "mcp_server_url": MCP_SERVER_URL, "token": github_token, }, ) print("Created credential:", credential.id) # Upload a custom skill skill_md_path = os.path.join(os.path.dirname(__file__), "greeting-SKILL.md") with open(skill_md_path, "rb") as skill_file: skill = anthropic.beta.skills.create( display_title=f"comprehensive-greeting-{int(time.time() * 1000)}", files=[("greeting/SKILL.md", skill_file, "text/markdown")], ) print("Created skill:", skill.id) # Create v1 of the agent with the built-in toolset, an MCP server, and a custom tool agent_v1 = anthropic.beta.agents.create( name="comprehensive-example-agent", model="claude-sonnet-5", system="You are a helpful assistant.", mcp_servers=[{"type": "url", "name": MCP_SERVER_NAME, "url": MCP_SERVER_URL}], tools=[ {"type": "agent_toolset_20260401"}, {"type": "mcp_toolset", "mcp_server_name": MCP_SERVER_NAME}, { "type": "custom", "name": "get_weather", "description": "Look up the current weather for a city.", "input_schema": { "type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"], }, }, ], ) print("Created agent v1:", agent_v1.id) # Patch the agent to v2 by adding skills; each update bumps the version agent = anthropic.beta.agents.update( agent_v1.id, version=agent_v1.version, skills=[ {"type": "custom", "skill_id": skill.id}, {"type": "anthropic", "skill_id": "xlsx"}, ], ) print("Patched agent to v2:", agent.id) versions = anthropic.beta.agents.versions.list(agent.id) print("Agent versions:", versions.data) # Create a session pinned to v2; the vault supplies the MCP credential session = anthropic.beta.sessions.create( environment_id=environment.id, agent={"type": "agent", "id": agent.id, "version": agent.version}, vault_ids=[vault.id], ) print("Created session:", session.id) # Send a prompt and stream events, answering the custom tool if called print("Streaming events:") anthropic.beta.sessions.events.send( session.id, events=[{"type": "user.message", "content": [{"type": "text", "text": PROMPT}]}], ) with anthropic.beta.sessions.events.stream(session.id) as stream: for event in stream: print(event.to_json(indent=2)) # `get_weather` is a custom (non-builtin) tool, so the agent emits an # `agent.custom_tool_use` event and expects a `user.custom_tool_result`. if event.type == "agent.custom_tool_use" and event.name == "get_weather": anthropic.beta.sessions.events.send( session.id, events=[ { "type": "user.custom_tool_result", "custom_tool_use_id": event.id, "content": [{"type": "text", "text": '{"temperature_c": 14}'}], } ], ) if event.type == "session.status_idle" and event.stop_reason and event.stop_reason.type == "end_turn": break if __name__ == "__main__": main() anthropic-sdk-python-0.120.2/examples/agents_with_files.py000066400000000000000000000044521523216435200236370ustar00rootroot00000000000000#!/usr/bin/env -S uv run python import os from pathlib import Path from anthropic import Anthropic def main() -> None: anthropic = Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY")) # Create an environment environment = anthropic.beta.environments.create( name="files-example-environment", ) print("Created environment:", environment.id) # Create an agent with the built-in toolset and an always-allow permission policy agent = anthropic.beta.agents.create( name="files-example-agent", model="claude-sonnet-5", tools=[ { "type": "agent_toolset_20260401", "default_config": { "enabled": True, "permission_policy": {"type": "always_allow"}, }, } ], ) print("Created agent:", agent.id) # Upload a file file = anthropic.beta.files.upload( file=Path(__file__).parent / "data.csv", ) print("Uploaded file:", file.id) # Create a session with the file mounted as a resource session = anthropic.beta.sessions.create( environment_id=environment.id, agent={"type": "agent", "id": agent.id, "version": agent.version}, resources=[ { "type": "file", "file_id": file.id, "mount_path": "data.csv", } ], ) print("Created session:", session.id) resources = anthropic.beta.sessions.resources.list(session.id) print("Listed session resources:", resources.data) # Send a prompt asking the agent to read the mounted file and stream events print("Streaming events:") anthropic.beta.sessions.events.send( session.id, events=[ { "type": "user.message", "content": [ { "type": "text", "text": "Read /uploads/data.csv and tell me the column names.", } ], } ], ) with anthropic.beta.sessions.events.stream(session.id) as stream: for event in stream: print(event.to_json(indent=2)) if event.type == "session.status_idle": break if __name__ == "__main__": main() anthropic-sdk-python-0.120.2/examples/azure.py000066400000000000000000000007351523216435200212670ustar00rootroot00000000000000# /// script # requires-python = ">=3.9" # dependencies = [ # "anthropic", # ] # # [tool.uv.sources] # anthropic = { path = "../", editable = true } # /// from anthropic import AnthropicFoundry cl = AnthropicFoundry( resource="your-resource-name", api_key="your-foundry-anthropic-api-key", ) response = cl.messages.create( model="claude-haiku-4-5", messages=[ {"role": "user", "content": "Hello!"}, ], max_tokens=1024, ) print(response) anthropic-sdk-python-0.120.2/examples/batch_results.py000066400000000000000000000006171523216435200230020ustar00rootroot00000000000000import sys import time import rich from anthropic import Anthropic client = Anthropic() try: batch_id = sys.argv[1] except IndexError as exc: raise RuntimeError("must specify a message batch ID, `python examples/batch_results.py msgbatch_123`") from exc s = time.monotonic() result_stream = client.messages.batches.results(batch_id) for result in result_stream: rich.print(result) anthropic-sdk-python-0.120.2/examples/bedrock.py000066400000000000000000000024311523216435200215450ustar00rootroot00000000000000#!/usr/bin/env -S uv run python # Note: you must have installed `anthropic` with the `bedrock` extra # e.g. `pip install -U anthropic[bedrock]` from anthropic import AnthropicBedrock # Note: this assumes you have AWS credentials configured. # # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/credentials.html client = AnthropicBedrock() print("------ standard response ------") message = client.messages.create( max_tokens=1024, messages=[ { "role": "user", "content": "Hello!", } ], model="anthropic.claude-sonnet-4-5-20250929-v1:0", ) print(message.model_dump_json(indent=2)) print("------ streamed response ------") with client.messages.stream( max_tokens=1024, messages=[ { "role": "user", "content": "Say hello there!", } ], model="anthropic.claude-sonnet-4-5-20250929-v1:0", ) as stream: for text in stream.text_stream: print(text, end="", flush=True) print() # you can still get the accumulated final message outside of # the context manager, as long as the entire stream was consumed # inside of the context manager accumulated = stream.get_final_message() print("accumulated message: ", accumulated.model_dump_json(indent=2)) anthropic-sdk-python-0.120.2/examples/data.csv000066400000000000000000000000401523216435200212020ustar00rootroot00000000000000alpha,bravo,charlie 1,2,3 4,5,6 anthropic-sdk-python-0.120.2/examples/fallbacks.py000066400000000000000000000045471523216435200220700ustar00rootroot00000000000000#!/usr/bin/env -S uv run python from anthropic import Anthropic, BetaFallbackState, BetaRefusalFallbackMiddleware from anthropic.types.beta import BetaMessageParam def main() -> None: # Server-side fallbacks (preferred): the API retries a refusal itself — one # request, a plain client, no client-side logic. Use this when talking to # the API directly. client = Anthropic() message = client.beta.messages.create( max_tokens=1024, model="claude-fable-5", messages=[{"role": "user", "content": "Some prompt that triggers a refusal"}], fallbacks=[{"model": "claude-opus-4-8"}], betas=["server-side-fallback-2026-07-01"], ) print("server-side:", message.model) # If your provider doesn't support server-side fallbacks, register the # client-side middleware instead: fallback_client = Anthropic( middleware=[BetaRefusalFallbackMiddleware([{"model": "claude-opus-4-8"}])], ) state = BetaFallbackState() # pins follow-ups to the model that accepted # Streaming: on a refusal the middleware retries and splices the fallback's # events onto the open stream — one continuous message, with a `fallback` # content block marking the model boundary. messages: list[BetaMessageParam] = [{"role": "user", "content": "Some prompt that triggers a refusal"}] with state, fallback_client.beta.messages.stream( max_tokens=1024, model="claude-fable-5", messages=messages, ) as stream: for event in stream: if event.type == "text": print(event.text, end="", flush=True) elif event.type == "content_block_start" and event.content_block.type == "fallback": block = event.content_block print(f"\n--- fell back: {block.from_.model} -> {block.to.model} ---") streamed = stream.get_final_message() print("\nstreaming:", streamed.model) messages.append({"role": "assistant", "content": streamed.content}) # Non-streaming: reusing the state keeps the conversation pinned. messages.append({"role": "user", "content": "what did I just ask you?"}) with state: follow_up = fallback_client.beta.messages.create( max_tokens=1024, model="claude-fable-5", messages=messages, ) print("non-streaming:", follow_up.model) main() anthropic-sdk-python-0.120.2/examples/google_cloud.py000066400000000000000000000024741523216435200226050ustar00rootroot00000000000000# /// script # requires-python = ">=3.9" # dependencies = [ # "anthropic[google_cloud]", # ] # # [tool.uv.sources] # anthropic = { path = "../", editable = true } # /// # Claude Platform on Google Cloud — the first-party Anthropic API served through Google's # gateway. Authentication uses your Google credentials (Application Default # Credentials by default), so run `gcloud auth application-default login` first. # # Configure via arguments or environment variables: # ANTHROPIC_GOOGLE_CLOUD_PROJECT GCP consumer project id # ANTHROPIC_GOOGLE_CLOUD_WORKSPACE_ID Anthropic workspace id (wrkspc_...) # ANTHROPIC_GOOGLE_CLOUD_LOCATION override the GCP location (defaults to "global") # ANTHROPIC_GOOGLE_CLOUD_BASE_URL override the derived gateway URL from anthropic import AnthropicGoogleCloud client = AnthropicGoogleCloud( project="your-gcp-project", # or ANTHROPIC_GOOGLE_CLOUD_PROJECT workspace_id="wrkspc_...", # or ANTHROPIC_GOOGLE_CLOUD_WORKSPACE_ID # `location` is optional and defaults to "global". # Credentials default to ADC; pass `token_provider=...`, `credentials=...`, or # `access_token=...` to override. ) message = client.messages.create( model="claude-haiku-4-5", max_tokens=1024, messages=[{"role": "user", "content": "Hello!"}], ) print(message.to_json()) anthropic-sdk-python-0.120.2/examples/greeting-SKILL.md000066400000000000000000000002341523216435200225630ustar00rootroot00000000000000--- name: greeting description: Replaces ordinary greetings with nautical ones. --- Whenever the user greets you, respond with "Ahoy!" instead of "Hello". anthropic-sdk-python-0.120.2/examples/images.py000066400000000000000000000013321523216435200214000ustar00rootroot00000000000000from pathlib import Path from anthropic import Anthropic client = Anthropic() response = client.messages.create( max_tokens=1024, messages=[ { "role": "user", "content": [ { "type": "text", "text": "Hello!", }, { "type": "image", "source": { "type": "base64", "media_type": "image/png", "data": Path(__file__).parent.joinpath("logo.png"), }, }, ], }, ], model="claude-sonnet-5", ) print(response.model_dump_json(indent=2)) anthropic-sdk-python-0.120.2/examples/logo.png000066400000000000000000000345321523216435200212370ustar00rootroot00000000000000‰PNG  IHDR Z@!2R pHYs.#.#x¥?vtEXtSoftwarewww.inkscape.org›î< IDATxœí}¸%EÑö Ë. ìzï9ÓUsîeW?”EüñCÁˆ¢ˆ  ’L€¢ Qï"†•$‰Šb@I"bF%H2|”ŒawoøŸ»†ÚÙsï=gºf¦çœ®çég {z:TW×Û•ü¡ÁªÐ'Ö9P @½NCó†mëG¹7(æ¿ ÀZƘç"âàDü*"^€{ñQ˜DÄ•ˆøÜ×ÀŒ1Ÿ€à5Q=s```A›ïòÚªžÿéŸÕÿeÿÇøøø¨1f³(Šö€ãñlø"ÞÀ¿qVÀCˆøWø%œ…ˆG7›Í]IŒ?¥Óoö%4Æl„ˆÀðî¼ ÷GÄ÷Ó¦Øþëvñ$ã¢h©1æ ×õ°ícÌ{`ï%K–¬9àá|GFFcÞ ïq˜ãûq¼ch4-û}ZóÐfY:g´Þqï(×°›õnµZëÀˆøjDÜ.o€íÀäG©»Ð9¤µsXs:Ç»0¼dÝšÍfLëÇñ«q[Ÿ[ÇÛ4›Í-à…V™^ÒjµóçPÊzýRÊ*ÿ£££ £(Ú€ ñ.DœŒãxºÕjMÓŸÜHai×øÿÓßçßÀrø3| ¢hãÌú³"ãí¹ä±ÅqÜT¸kKiV†ìmŒysǯo6›[6›ÍõŒ10Ã_†B™ôÝh4žeŒ9ØŽÑE;·}÷ eÏ1í¯1æuð¸DÄ©ì9žé gϱ=Óp;\@kN²4óÝÕÆÑK”0"žgd¥†9Úõƒˆ¿´ýû,øÚÑ<Ëho¶ë1á¸Ü&íºì/¿ãËþ“ðŒãøQ;Ö)‡ùÝ–CX%‡«Ñh¼L|?Ïú©%ç ®ÈqΘÇß¡Àãɾ‘{é%k×Ùù&ŠŸÿA¬·–|KÖ-Žcm¹Sx㋞€;iàbDü¬1æíQ½˜82ëÅ`¤n÷Ãl´Š¢@Šh³Ù$~ü]*B!™@ÄöÏIÛ¦2JË”müÿWÚ6%û´¼IÊ̵ˆxbÇ›Y<èñz'kÖl67±s©ì·ûDkÜ?€OFQôFk¥*&ßhµZÏBÄ'×’eÝ5kˆq÷ Ëý"`Œˆo€ï’UÞµd]ÅùÌžã©9Îñ„=ÃôÏY^Z WÀ‘ë®».ñNÏ‘d‘ÇÇÇ—ÀcvqVÚÅÉÛ’ßÛÅ|‰ýN-+"îl™a¹ãzÈu¡þîµÉ—W¶€ý"šO@Žù­ õ€[r*¾€^*¾ïʇ½Þ–[þ¼$/€}m+„rÓmKÎ:¹ƒÈ½ô€\%æ;áÀãWeûV dÝàM {RfãKue8s¡NÙ¾ï%IÈ¿º¼ÐÏECr£££ãäŽ7g×AÜí”È ++¦¬«äßÈ""ÖØkH¯™š% ï[>¹@¡°Nið¾Ôë®qÔëx¡˜X¹W=DÃÂj´Œ¬´Bé÷á³W?â›ßé¿éÑùUAÊ“)Hï”ß« @øõ)àÔ~³J ¤ú Êå| ÈÜHõüÚîRMc+oqD¾[CÒKŸ¬8ôÐÆñ26ó6™Yï+Z­ÖÆb>U*–½@²¼Ÿ…7 ²†eŠÝ9·|é¢Ã¬´cEÑξƒ>ßNø¯¹ œõéíòûS™$MÛG50*ZŸ@ª ¹ÏU Q õk‰²D5¨‚xE²qã>àlQŸeªGåI »–¬ÄÒ^¶ßá˜}*õtQ°ØqáÛCeß5¡díFGGÇD*ö•½&/íù¥y½Ñg]›ƒÏÏ-)ß<ŸÙï H{aó2 V°F€T/—òáis`‹lE Ÿ"ç/¿Ä®á`ŽàÄ}xœ++û’2µõ+)“‹¾Xùõu¹^¹]mˆˆˆ5,JÞóþ$ßà‚ts5^²q± Ðr}j’†·—xŸ‹ÎåÍÇ)yè“Êè >v-)‘ÐJ>‹íÎ+­×g¸0™i¿÷6¹.•n UN¬@±Î¾ð H{fNLßÍfs½’^¼ FãeöÀR›(¶e4M>/kÌqsV’+ì23õ%)‘³g))’¥|±òÚ_#bä òøyõ±ØÒ±Š|€à* H±ñ³ˆx""~¾_€+ñ6øw‰Š Çàì[`|¢:©˜÷µ÷‚&/ÊðjWëÛ'•Ñ™—–Æq|Øís1ÉV±ÿSð Ô€ àËðqŽO!w0*6IºV›3Oi4Ï2ÆìfŒ9-³ÞEXD˜—î¦;¢ ;I€ðCçYóþÎQíeSÜ~‰\¤àþ‚xJÌí¿sîzetº?eßžeyýnÑç8þÏ]ð8\‡EQôb0×0LõT¢(z=,PÀ8Ë„ÁëùU‚EV8-¸òyGÊ5"~Ž7bÀ?ª€¤<Š¢=JX'H”Ì;ŽãÅ ¯BüJøÅšäH¤·óãöeËÙE‹c¶Fįñ+Ÿâ¥Ê—é)Êóbxc…Lj•û°ô8û7<ÃX†gh«)TX-ŽãÝ)]«U~S©€{ûÊë^á±UÐXg¢A0ˆ¸"~OøàO(» žs^,_¶©×*£3ÈÚ« ÝmRr8Axª´+^:Ó^åî°)‚%Ïey™Ý¯«Ø/žìdrtApÞ@ûý».\8šŸ/äaÓÙQa€ž¤èWI-1“iÒVxÍ;ïô€“ÉÕÎcA‰óp)Pb@z€ð…³“í{~ |8Oðb²&‹-zº1æ<ÅKUº,P\ÿ¤R ù4fæ›óq™øæà ŠÉ\c•ÊMúßÉ" ·Ø9hºÀ%Êªí·ˆúêŽ+™÷W‘ƘÍDêW•x.ËC¿ËðB§4(^߯sÔyoö¨2z2¿Å‹PÝ ôÜ Ûç 8)“LIžãN÷DžaN<&Ò%kZ3Ynþ¾ ·¹DPàP@¾.‚a•$”*Ççù@$ßVF '¤L’~±®„_œ¿dû¿¨ßb@~ŒãxGÙwI´ÚelŒy¿ÒÝ‘þžÅ•ä"¯ûk ÷,ÃïÕ‰y}´äù*ýQÍz|>¥<—_ÔÄrl¼¿ 0\¶lež:]‰¯8>t%¹àÉuë‚z¹2zš:»×«•üðÇñ&œã!)/£(ÚŸ’ÌØok‰L,×pÙ>qgk×…0ÆäIÚO$a>Ë4;È4€²`uwé\¶°Òó%yÊv×”‰à­,ïYi¡”÷϶Ÿ™WàÅ¿ŠBdŒy»Œh+1äòe¿©5—^ ’R…’,_J¼åZG>º=¢X½ˆ¤Ý¯óÓÉÉq^ÓíÖÏ'po¿'­½EÉË×P² ^k…yð~]§l=îøuÛõ•7i”­ÃqAøû“Ífóyrœžä&ò­.ˆi $îÎc S"¯–.]º€ÜG\#XÁYÖBÔ€${f×ê$;¾BÌßÁËé¬PÄ•zÅkF™Çñ;4æfåá×æÆ±—*Êzvuø"F cÌK Bö²ãšô¤&E?iTPŽùžqÉYDeôs*|Hf·%¤ÀpGÞ™n#gÎߪ@Ý_1q”KñâüÁç”wXÉ÷ùfê²ý8&îïo¾ìû@´üž)mãËŠ Äi¯ Ä•z€¤®”ÊQ.ò%z±ã½Áëý"1Y ÷6›Íõåw*V~OPx€Ì®ÿöZ>ð½ @äÜVhð>üÖÁ•Z$©Ê}TFçsü^Moý‡‡~9>>þ”Šæ•Î[”uÑ âDzƒÏ®eü$³Õ:°}:&D|«oZ@T²¿ë‡8Z€-à‚\°º<‡!=·Òs«ã}Ä÷Ư³ýç¼Üù1M-S‘HwYu`nš¡.V†~BdôrUÎz€0oÎ#Wj‡`DüKEkgúÏÃû‡idÜó 2ú B‚‘é ßÝÛjµª~DÖÛ•æÇàêcEž™Ì5øœ7äQDÜÀö=O¤˜›¬ø%ËwòÈhàÒX°SD`°€¬Âë]ïMÈ‚¯QæKM Y°cÀQÙç`ô³´*£#bÙ•ÑùŸ¤Äד6¾é×ÂzXõÃxò}ò|1ÆPF½iëÊ7áÐþ‹¢hó¢•èC™ë ¶ï€ŒS y a!²: ÷±s;uttt\¤TsAè /W<€¬à‚Õ…, .X]S"§–,Y²&"þÙ,VÏQògYÿaO^M³”¬O£ÑhQ ®‚Ň»R)“e_cÌW4é ŠÙz°BÍ>“çʾËRÌà%÷¤I @^Wâ<ºáŸwÿÄqÌé°ó6¶`ž\Ä`eÅË«•|éÒxªèŸ‘çYJ¯YY”OX@8CùHjæãø½J‡$@éN^…2¦Ùl®gcReÊA®^šSÞq•ö§À Š +…Çü†gÄ KÕ BÁ¿²ÿœÔëdH ð¦ÈÀ8òšÔ¯­aetv#{¡#¿Lgdû¯<³^¦{Enwä‰WP&+DüIÎv)¥§ø·"›¸8ŽÙºøÅq1¥Od^vX˜½Z+ÀÝ¡¸Žï.Xì¼eUP!¼—Ñ…®õú\°Ü.½à‚\°©—]°™EÑ»µÒðcÎÉ97^çW*ÜÉ\¬ÂûeOÖz®šËÀ5“¼ß.”ý»Œ­‡HBàzÿóšüsñâÅÙC9KÚ5¬ŒÎzÚÑš»Æ˜·ûÆ7‚ ©W2ÃRõ#xm‰c3UPr™Ù÷‘}·aÜ à1G¡Áfì<â+Ê’ôÛh4ÖQð¹L~kû¸}tttÌ5%_pÁr»ô‚ l/ø|гÆ/}®¹âéLž¤Àƒ(Š¢ã•‚DåÝTʯ«·+Zc¾ä‰²Û¢?¡h‰—Æ#XÖ“.Ñr?•LJuÆ×Ô¨2:óËÆ‚O\øxeF×õ-‰D-H¼¹F)øü®9‚øp] ”Ûý6Bµ³|¯ÎD¾n-Í”ÂxºøF·H°€L¸¾z!âv×Ç**Ê…TF§ÍAÇ®ì¥è~õDÇÏ.pÌ=OÙàsg$Çñ)²ï™¾)”uç}xÓ߬3Iÿ€S”sWçMH ÎÊ!)åˆø¸ˆ”NÛ…àZ©.deÆ*U–,"klE;Àùï&æâš¥&•©/À¼Æ¯SÌ~µ2Š¢gòüü'agh]¸Äöb@žÌÊJòËmg¹³Šü‰¢h[åsͺãá : óÄÊc”Äk{‚/'Up‘é72`ôÜÒ[bÅöL¥L @6Ÿƒ‰ø¥k-²^(£_ØÃlJ5לøéBÄ[sˆ3É™¼Ôæ:Çç§1¯¢hcÌîšÍöù6cÌAƘ Pa:Š=ë=¥dùÈÊÔ]22µ[Ù~‚‹rš¢žì•y¡HJÖÀs°âÜâ]¤¤h—Ð!ÛæeïxkùxTœ…itÏãããcÊ€Wêu¨Œ®·2-5Γ}ʱ!­Vk]­àsD¤|ÈÝõk£c6ªŠ é?ÓEªešµkrŽ‹9@4”ĉ´©> I«äqµ] «Gvíÿ—jxä\{¶ØŸ¬¥¸x”½±Sâ»g{q÷L¹Væv,ŒW9Fη,ÚrË-çQŠg² )¬o;~»X®YJýÙu©ŒN:ª†µ¸õ7ÂÙnCüGʾìh˜ÐÞÝá†È€ WEšs¼´B7¬2È*^Ká õ€WÌð½™(@4_©{¹Õ €Xy\77ëÝÇñGº”o«É:ø–¢ââÿP6±¼.hÄÁ$mdddCÙ•ÄžÑã©O²ÌP|ƒv£D¯JÀ‹tcÌn”é þÇñ”2øHxÕö¹AºQ;~× ªE£ËXç¿)<àNØ>ö¬Ù9ö†xC†‚,™áì2³RòwñrGÁÎ|•fí²H2¯‘‘‘ňx·‚™–¿wõÀÀÀšòsP €Ò›¤W+twŽŽŽ;¬;_(¬1ËÛãj¦¸$ëÖl6cx××eoèÏ_EþÝÝTM®@E4J½L5P8¾ƒxÁäDN(Ÿ_žÓý¤/È5S$õÊè@ÛŸ¡<^YWå> þÿWó*ž¿Ò8û†øâÚJ(±®¯gv¹lÖÝWK°GQô†.ÇP7¢½nrÿŽëbí $(»¯@ô”ª<Ö×ÀV~(û£Ö˩畓gS4©ˆðßÖa2“q­j’´ âÊ&­KŸ¦ÕcþGÄSÖ¸ìÊè|f“}+ÔW\‹'Ns#‹ ì?P—ÇñZ¾{9²,pЕ€»_óY°¯"†(€ÈW9ÂX¼æËã8ÞDö? H ]ÈÇ`QU®:•£W9ûK~³lÙ²ù SR^ ssÈw_iP)/ïÏ^Ц:±¿Ÿ,¡år˜]ʶ¶±\¯¨ˆÊè×*WF—.„+”¬e“qeYêiJ+ŸSv †€ëä`­ô²<þÇ+ÊË\ÙP!€üîO;<ø€Hw22¸`«t¥w•Átñ 2%2\{•|Ç™v˜C¾{K .ÛíŠú@z¥±%á”’t!ŽM=O±F”fÚ`ߦ‚o<Þh4Z²ÿ@Ýeõ8DÃõɇ£r2 1m® D’±p YÉþµeùÍCR#¦B‹RdÊþg @ ¤³sHyŠWò‚)ôçrpÌÝ"¥*è\i«ä»—?WŒ…yí6½3ÀüÿwŠÙ))&–uÊí4À©ÕgÎVTî9äàEY‘WÆLÀCsÜ4[ÐBåsÞÀGšÍæú®Ì"Ò£¹Vb¿™jŒ”ÌUžÛð3EW¬Z­Ö³æØÏ@ ¤ ¹, ů3dzEQtHò³Jd,¥ð€;- .Áו\©‘ ̮öÛ@ü©{“‡XY +ÅzÈCŠ•Ñ‹ ’#¶rÜÉèßvdb:PA°MVŒ^Éš]}åëÃ÷eÿsu=ìð]¾o«É+‚ôwu­Í—ð—j’gžròƒ~i€¿Æºp°¢ìOdR£ÑX‡j‰(×(ޱT¢¸E¬ýl·€èœƒ–¿NªÀ5h8ŒîìY‡Ë¾Æöq_»Ç„…)¸`u¹_×Röã8~½#“$›G©Rü±`ûnÉŒQIÿ|D!¡€ÃÞ³Œ!@éNé 1 :JVVH€<¥6¥jëBfi¥á­i›àƒ²b>5³ge’f¡dÍÊèɸšÍæzd¹Pä• dÿµémÛIDAT:P’1ïSx)çÓS`Žô÷”ù@A¸±kˇ”Ææ;Iÿž1fk¥ÃÅã8c†H €tq‘¢ <¦¬ÂõÕ/`EÈúD‘3ÆüF+øÚsTMÜ.³µP"¸_±Û6¶ÿ@ròë;ƘsÅ9¨J!Nö‘J2=ε´Â¹ sâlvèQ!;¢AEÎ42½>(1¼f_ø7/Y²¤›êÞu éßEÄÏ+eÅš­XT €hÉ4ç9⼆éÝcÇ¿)vŠÜ=„<*´ÎÕ˜Ò¾¶ãÿŸš~Ù^FéG3z@îæX£¢Ÿ- SÂÝ|J¬ø5^½2:Ř*TFOƵtéÒšÉ$â8–.Ÿ:x!¥(øç„LàÁƒ •ø£´Š#cv,‰A| É![¸pá¨rѬ?SŸò€„ t ô™*ûÔ”…@Ü×0ÍŒƒˆ' à1¨ï1g­*Ë.;¬4^ åh ˆ_¶·ûQu…~ œéeÊM™WøA’•®p^Ø›åp-ÀÐð²iý§dÁ´ÆÕëÄ/§)(÷,D÷TVîS?= (t(Zºê@Ò¿EÑN @3=hˆø¹Ìx‚$X@4, ÷Q¾zD¼ÃÓv;".ï3 HâÖ¡Ü4*TÓÞCÉJ„Ì--¾²û(X–™þ¤;Yñ¼[k (–„²‹9¬A¿ÄÚa[bDDJû!±~¾€"+£óYtt#¼@Ñ’ùu9ç@³/üÿM®Ry§1lß”¥` ¥–TS€ïj£_Z“ø@V èW¸,ÒÂ^d=ýˆSÍËSo^¶lÙ|ʧÞjµ>µ8Ž›6ýª«+A­õCrC³Åqìú’ÌÍʳ¯É¹—¨|Æi’”8Ž×Hyá;åT-+üÔö7[S¯äΠ#9?vÍîAÄŒŒÈìK>ºÿ°~pŽ‚7©/$Ïñ'®QF=Op°Ö%eý@é…ð”-I¹ÝaMí®…/¬£K0“ù@’ABŠÖSÑëjQà‘ŸP$ÔÉs©LÕ Br¡pèÂã<“jicŽ&×GŠ®ŸÉï1›Éù—ä=ð|ÁS òtçç Bâ,h¼Ÿîx/@¦ nÙø²‰LKø‹Ý?í:QFÏË£(Úoíµ×n–èzèB O7›MŸ*£³Ž¶ŸâCÂc ñ)}Sù\#EkÚêâ{ˆ× ŒîIÇû(¼XÉKãö²Vâvéõs!B Û‹¾=kC}@VfödXññë]J_,X⥟ìÕèèèBD¼[H±ÿøgkrî€m €ÎÙÞ§Q?LÛRuÌ™Ujo¡ìOƘ·GQÄY‰åÛ|cÌõžTFg~y±¿LX>Þ½&ç¸Úàs­šÙ¼"›ÆÙ}èµr=ú€H×»ïk!~D|‚ª‰ò‚ÄéL, >‰LÖ»V®¹´5ìç)¹Ud:"–^̮в4QA¿š¸o°ûÕ^J 2™³ÙÜBöïʸtÜUP£˜²?Sð8í?"þ¿_€C›Íæ«[­Öú”­)3WNÒã;ð(´2zÇ.•Ñ“3Fîµð€¿¬´ó:Íö_§½) NWz ¯[ãKõ¼‚™ÄGÂÕ?××ri³c»’çk«ž>ìÐwZÐÒóK¸]€kµ×@ñÎDZvæ@¶‚ì¬ @®)1›åû'”âêøŸ]•ð2eÞµ,ŸäºmcH]öN€°žðqRþ©^Õ<Ñnããã£dI³|;¯ƒùÕ tZâñ*£§±FƘËþ‘qóŠðèÊt<¦„øêØR_½±±±õìº õ )"þ‡Çw(õÛl6 Ü’o- ¤ˆ´Æ^¬BØýá½%¹?ð:ï¨4þ™² z«DRíy§*¦ðð€ð~|°äýàXÙê 8fsß;G«´‚cet.ÆýQ¥ù ÛÏ[ÆT4iƒ¢¡n}¶ß£°ùunìs{TLâ+I…œ’û@ZˆÜ¯(%¦cÊä`éï„Þ„‘-”¹ù%ýz­.áõ‘_NA³8e5µ0||=å{íX%"q¥€Ã´,üÊäX1ï2âÌzøôÕ"vÊÉc€2kÙ¾‡<‚^¡ù‚ˆ—Ûþ}ÚSYŽÜü~†ˆ—!âOr¶KñðÅ®Fˆ¿QZìº6Vrop0ßÕ€° ýÃá(¨Ó1ÆqümJíì˜ë;@´ø\›ú- V‘$íd›’2ˉ1>YÐxgQÑËÎÒ˜‡¥tÔTG!ø~Jü¹©§c=–GµN‚D1/TFOƳhÑ¢§Q%½xŠþŒ¢h[žë€Äòv?Ãz[Þ¶rllŒæûÙwÓ8èžãÖ:R8ÿ_Ÿ»_¥Lb×ÁÅ|Wg’ ~£zZ1ºüÀ¥Rt €hò¹&¢OÉ>Çqüi-—+ËÎ-€pRª¯¤SÉã?U®ÄÖžeŽ.¶í,VÇ)Í5z?<<Ý1^4 b‡s*ü ¬·Uô š°²oóÄš™Z?43×!â­Q­Ýiº1”÷) º7žÿ¿è pYù@V±ŠÙC§asý} €hó¹RКRbQ$Õ¹8¡½ü·* „(Õ¦s°2O”—´"þ@ËÚcÿ\¡h­ ¤>Äüt®•ÑÓÇXª·¢å†?™ÝÍ¥V‰±›Ù¥|ÖŠu±ŸêÆÄô>%áÑ+ÑÅ|Wg’öG~| æÐäà9þ>@Šàs ¤bYyˆ²kÏ•¯¢RÒE¯R|=l£¼ ûx®¨?d H¬þ¢l0ºFeô³‚ÑSÒƒ¦…~y…ˆ5ªêüÆq|Júÿóçú8ì!ÅÂO½ÒÒüñ\É[ñ¢ª Iæ»Î:ë4Q ý;ïI¨ò$yL! –÷{ú€œ;™ò_ YnîSÒY¢ûõ·Ú.Ô'½\VB8MénÊE¹ ðëç, õ¡´2:"ªUFwF—5J\’èLge¨4²8a™ $ÑkqOEð!xæ¼»Ù½\qQ{¥¥æzòÿ“ëÕG¤]Nû*-d€ HQ|îJÁR?å[¨Ð›Ü¿Ǿ·²üL_P)ëQ‚+m“Rör•ŠãX|XÎó þ#Žÿ:RÁò©‘ΙÏñ©J‰y¦3òÿc¿3TÒýÆ„¯PªÙ“®µ]Ÿ·Êu›ËÔušV¶#›óbÀÙv½†ú€ÈÀ°Ó+!€R$Ÿ»P ÅÓ<Ê-¯é§lŒ)º˜+Ïk’5]ÙŠÌòÿšV«µ>¯Q¯¨\/韂hí¦´çD¯²Êû, õ"Þ¯¥ŠÁèäͲ '¨eè9T¨Úq<Ó3€o5u >ÇC¬6—ÀC¸‡^¿dÉ’5çZçä4›ÍX©XÿÞ§¦’³Ù1—t/„W(ý£­Â+@Šäs ¤Xâx ÎFãúÀÄ¿}\+ì7ŠRÜÙŠ¼[8|ÜKÖˆL`øPÀƒ¬FT`LÜZw+0¿R¶~R?*¢2úv®VDü|ÙbWÚñ]×jµž×îÜ)¬eÚWE{#âãE<ˆ â~¬1›`Þ­˜¹¢]¸ÚÉ¢ö(Iû€½éúÕä”QMœ> •Ðé§"‹ûñ™úrÁsH•hò.„rÅ3ߦÚ)⻬İòщBφÇ4Dw–ˆÅY©xç§.ÏÍf³ˆú[€ÔXáßN!+øß°}9dåGÄ:ÞçÓ3ésäÒ'EQ„3œãNA¹<ÃK‡¤/ ¤ýxpm'Ö”ñ× %ÇJ/´~e/«°Ñ÷_`Œ9XI¤ù®c@Ú)TT/&X@)ƒÏóP°€Oìú³™VJ[VN¨Où¢ÆÞl6·w­¦ò2%˜åt'Ð]<ÃY‘ŠI¶­6ÿñññÑ8Žwg]Áº\M(Ÿr]Ä9×?TB¯ ×Ëk•‚Ñu,­À è ÅŒXÓR§Ê<ÕúD³Ù\o†µ™éˇ†xc>€ˆw Ý­ðôF¹N³.")ë æl^¬›â8~j†qªVÖRÊœÂY9¶VR€j @ƳÈu¡€K´£}Y°žä!)dÁrãñkv«â©šÅ àG%Ü]¬¼œTò’¾óàwˆx"%R¡—ÛNäÕ<ˆãøÙƘݩ81½öÒx x5ÍêwX!zß#‡)œwÊèLÈxyA1±SòÛΗҸ£(Ú<ŽãfrjØs|œØõ+âñ ë´Ù,¿ª˜aàöï³¼®”Ù’Üʈx‚â¿Þç$ý†1潤, eñy·, å[AþK±Âød7¯w®<222²ˆ|½ ”Ÿ«(0ÜH €à"Dü"~?Šˆ€/À™ðsø3)=ü;¾k P\fJ‹\ÄúROb¯‹%ZÁèdMq¬$yéQ%4Ýn¼ìæ(Îâ”uÿºÖó=D<>cèÑó}Dü#­W›3¬ýxZ–Œ1Éõ™‰’#?¶{·\øVUÑq6&yBižØW¤´ÿ> ©Ob/³ `y’‡<¦`qãñ`™ƒ·â8þˆRöFYói~IiyÉUùqÇ{©£¹IÅC’Ùß/)-sÕB(‚©/ñƒù9ŽúFgÇqÞÊèÙ1í§œ–wz†q§g±Ýye å,g¸(ñ5wºž,TPY¾È3ð‘}•ü±Æ\­ [¾%äQÏ1ƒ5Òa¶Ës~1Žã—*øê§YFGGÇ7® 7¬ÅJÅ«Øëe9ÿ^ é·©rn¾ÌÁHÕ|> Hõ@ÿ9е5ØÊ¿g‰<7Èß¡øD¼_XCêøŠšÖ-°EÓ¢×2K€ôñÃÖንÑSÅü˜0hŒù %e¤ÆUÕ·8Ön¢hã¼ëĦ«S5ª‚ÛÅR-eÓÚj'¼¥ä,‰+ý½ÌÀ¥ÂR 2!î¦ÇŒ1ïs«ê±2Þ ~lxzA•Ñ5Ç9þ¡ÙlnIrDèD¾–5¬o}s||œe`¾u"Åîqܰ"‚wʾ ¶ŒàjºWä@ìs’~Ÿ” H$ø¼âÁúŒŒ•ûn¥â„üú÷¡ ø.­5eŒy'%Ž÷–¯@„ÇÐ\‚ˆËÄ|ªŒ ¤wˆ].ÏU¨Œ>©T}V‹&¬Ÿ€å‘xX¹w_³ÙÜWåñ€‚ÏãnX V·b67*VÔ< ‡YªWˆ|¡8¿H ¾ðy–©žø²?R«8¡bѲ<4$g¤Tò¤Àع¥Ê‚OÀÃÞ‰·À›2ûRµžHï£kèqEêPÂÿ6¦”ÛôørŽ9ÖŒÁØy QÈBvüyI+×þ8F ¬ŽŒ{ŒBš4¶ýJfÚêp=@l:ºû3¹k $_øo@~˵rÎ51ÑS?96:µ쪰'OP­V««bT%fä{‡ÂYÎ'ûbŒyQ…÷Þ"V¦œˆˆ¯©¬DðØµ Ã2ÈYÉeÂÍj…­Áòf¹®‰]×3º\VÎÞä8Þ“s<„Cˆx™#›÷\Zâ›H¯›ž]n3ÑWHãøa09ç¾’xM$>¨AÄw)œ³dß)ͳ§|>ÀühŒ¹Þ‘ß™ÇÿP¡<î {’ðcÇ¾î ¥êüµ’ìIä<"þ¬€=É D„oùôwfÀ7©xÈbbY…•þûi¬‰\úïð;8Îó\16GÖ2ÿߎr8å}š¿§¼ßÄ€r)">⸧‰Ìãøú5mÿEóð°ü†1豿G…®i\­VKžc¶X¬çxjŽsÌVÊÄ:Äóe÷TJõNŽÖ0³®ºüŒˆcŽ&ßXcÌò48>ŒˆÏ­#±R¸Ò¼Ö„Öóƒ6d8GÀäºÆ˜ÝÈ vé¶ÑïŒ1»À e¿ž½%qïžwŽmæûfñî;ñkåZˆ¸Nz}ÎÙv1ƼEœ?Ÿöz®Çc­ü82g£3z´”ÞÎö6Æã0ß#ìï÷.мLAöÑüŽoµZë˾=!žç¦4F—yfÚѤ(xƒ«) £££ ›ÍævÎ?DÄ»ØmÕ*XD´mRIáßP܉u¯:2ZYÐ1\à±ZÁ\{ˆî4ØPö¨2ÆlmõWc×¥K—.,yO‡²ç˜â½¢(ÚÉóYªE±›TžslÏð$ü¾kŒy›3¬<ÍJAhOI.ìÀ‡*™úEö°ò¿šò@ÅÐ(èÕ*f‡#âWñ'”„Ås-ÂFŠÉ xØZP®¶àå3ô°FÙ.É‚»lÙ²ù~7P ©WÎøàLnO­V«AæQíaó΀ŸÓ£¹n‘ ûP@`寸œ‰ˆGÙÇË„‹äœßÔ¤ÿà]WÔ"ŽIEND®B`‚anthropic-sdk-python-0.120.2/examples/managed-agents-observe-tool-calls.py000066400000000000000000000217761523216435200265360ustar00rootroot00000000000000#!/usr/bin/env python3 """Observe every tool call with the low-level session tool runner. ``client.beta.sessions.events.tool_runner(...)`` is the "observe every call" path: an async iterable that attaches to a session's event stream, runs the matching local tool for each tool-call event, posts the result back, and yields one ``DispatchedToolCall`` per completed call. It does NOT manage a work-item lease — ``EnvironmentWorker`` is what does that. This file has two scenarios: main() — the primary entry point. A session you created and drive yourself: no work queue, no lease, not necessarily self-hosted. Create an agent + session, send a prompt, then iterate tool_runner and print each dispatched tool call. Reach for this when you just want to see (or audit, or react to) each tool call on a session you own. observe_as_self_hosted_worker() — a second scenario, NOT called by default. The shape to use when you *are* a self-hosted worker: it composes the work poller + the agent tool context + your OWN heartbeat task running in parallel with the tool_runner loop. Reach for this only when you need per-call visibility AND lease management together — otherwise ``EnvironmentWorker`` already does both for you. Security model: the tools execute bash and file operations directly on the host. Run inside a container or other isolation boundary you control. """ from __future__ import annotations import os import sys import asyncio import logging import anyio from anthropic import AsyncAnthropic from anthropic.lib.environments import MANAGED_AGENTS_BETA from anthropic.lib.tools.agent_toolset import AgentToolContext, beta_agent_toolset_20260401 MODEL_ID = "claude-haiku-4-5" def _require_env(name: str) -> str: val = os.environ.get(name) if not val: sys.exit(f"error: environment variable {name} is required") return val async def main() -> None: """Primary scenario: drive a session yourself and watch each tool call. No work queue and no lease are involved — we create the session, send a prompt, and consume ``tool_runner`` directly. tool_runner is passed no ``environment_key``, so it authenticates with the client's own credentials; that makes this scenario work against a non-self-hosted environment too. """ logging.basicConfig(level=logging.INFO) client = AsyncAnthropic() environment_id = _require_env("ANTHROPIC_ENVIRONMENT_ID") workdir = os.environ.get("ANTHROPIC_WORKDIR", ".") # 1. Create an agent with the built-in agent toolset. agent = await client.beta.agents.create( name="observe-tool-calls-example", model={"id": MODEL_ID}, system="You are a test agent. Use the available tools to answer.", tools=[{"type": "agent_toolset_20260401"}], ) print(f"created agent {agent.id}") # 2. Create a session. A session always lives in an environment, but here we # just create it and drive its tool calls ourselves — there is no work # item and no lease to manage. session = await client.beta.sessions.create( agent=agent.id, environment_id=environment_id, title="observe-tool-calls-example", betas=[MANAGED_AGENTS_BETA], ) print(f"created session {session.id}") try: # 3. Send a prompt that will make the agent call a tool or two. await client.beta.sessions.events.send( session.id, events=[ { "type": "user.message", "content": [{"type": "text", "text": "Run pwd and then ls to show me the working directory."}], } ], betas=[MANAGED_AGENTS_BETA], ) # 4. Observe every dispatched tool call. tool_runner attaches to the # session's event stream, runs the matching local tool for each # tool-call event, posts the result back, and yields one # DispatchedToolCall per completed call. AgentToolContext gives the # tools their workdir; passing client + session_id also downloads the # session agent's skills into the workdir before the first tool runs. print("\n--- dispatched tool calls ---") async with AgentToolContext(workdir=workdir, client=client, session_id=session.id) as env: async for call in client.beta.sessions.events.tool_runner( session.id, tools=beta_agent_toolset_20260401(env), ): print(f" {call.name} {call.event.input} is_error={call.is_error} posted={call.posted}") finally: # 5. Clean up the session. await client.beta.sessions.delete(session.id, betas=[MANAGED_AGENTS_BETA]) print(f"\ndeleted session {session.id}") async def observe_as_self_hosted_worker() -> None: """Secondary scenario — NOT called by main(); shown for reference. Use this shape when you *are* a self-hosted worker (you poll a work queue and hold a work-item lease) but you also want per-call visibility into every dispatched tool call. tool_runner does NOT manage the work-item lease — EnvironmentWorker does. EnvironmentWorker.run() / .handle_item() already run a tool_runner internally while heartbeating the lease and force-stopping the work on exit, so reach for them unless you specifically need to see each call. Rolling your own heartbeat, as below, is the cost of getting per-call visibility AND lease management together. NOTE: the heartbeat loop here is a deliberately simplified shape — just enough to keep the lease warm while the session runs. EnvironmentWorker's internal heartbeat is the careful reference: it bounds each request, distinguishes transient from fatal failures, and assumes the lease lost after a TTL of failed beats so two workers never end up serving the same item. """ logging.basicConfig(level=logging.INFO) client = AsyncAnthropic() environment_id = _require_env("ANTHROPIC_ENVIRONMENT_ID") environment_key = _require_env("ANTHROPIC_ENVIRONMENT_KEY") async def heartbeat(work_id: str, env_id: str, stop: anyio.Event) -> None: # Scope the environment key onto a sub-client so each request carries # `Authorization: Bearer ` and no `X-Api-Key`, inheriting the # parent's timeout / retries / http_client / default_headers. This is # what `EnvironmentWorker` does internally for its heartbeat. scoped = client.copy(auth_token=environment_key, credentials=None) scoped.api_key = None # The first beat claims the lease with NO_HEARTBEAT; later beats echo # the server's last value back. last = "NO_HEARTBEAT" while not stop.is_set(): resp = await scoped.beta.environments.work.heartbeat( work_id, environment_id=env_id, expected_last_heartbeat=last, ) last = resp.last_heartbeat if resp.state in ("stopping", "stopped") or not resp.lease_extended: stop.set() return # Beat at roughly half the lease TTL; wake immediately if we stop. interval = resp.ttl_seconds / 2 if resp.ttl_seconds > 0 else 30.0 with anyio.move_on_after(interval): await stop.wait() # The poller claims + acks each work item; with auto_stop=True (the default) # it also calls work.stop when our loop body returns. The lease heartbeat is # the part tool_runner does not do, so we run it ourselves alongside the loop. async for work in client.beta.environments.work.poller( environment_id=environment_id, environment_key=environment_key, ): session_id = work.data.id # Passing client + session_id makes AgentToolContext fetch the session's # resolved agent on enter and download each skill into the workdir. async with AgentToolContext(workdir="/workspace", client=client, session_id=session_id) as env: async with anyio.create_task_group() as tg: stop = anyio.Event() tg.start_soon(heartbeat, work.id, work.environment_id, stop) try: # Pass environment_key so the event stream / list / send # calls authenticate as the environment, like the worker does. async for call in client.beta.sessions.events.tool_runner( session_id, tools=beta_agent_toolset_20260401(env), environment_key=environment_key, ): print(f" {call.name} {call.event.input} is_error={call.is_error} posted={call.posted}") finally: # Stop the heartbeat and tear the task group down once the # session's tool calls are done (or the loop raised). stop.set() tg.cancel_scope.cancel() if __name__ == "__main__": asyncio.run(main()) anthropic-sdk-python-0.120.2/examples/managed-agents-self-hosted-sandbox-worker.py000066400000000000000000000157601523216435200301760ustar00rootroot00000000000000#!/usr/bin/env python3 """End-to-end self-hosted environment worker demo. Creates an agent with the built-in agent_toolset_20260401 plus a custom ``current_time`` tool, opens a session against your self-hosted environment, sends a prompt, runs an ``EnvironmentWorker`` in-process to service the tool calls locally, then prints the resulting transcript and cleans up. Required env vars: ANTHROPIC_API_KEY your standard API key (used for agent/session calls) ANTHROPIC_ENVIRONMENT_ID the self-hosted environment to poll ANTHROPIC_ENVIRONMENT_KEY the environment key (the worker's single credential) Security model: the worker executes bash and file operations directly on the host. Run inside a container or other isolation boundary you control. """ from __future__ import annotations import os import sys import asyncio import logging import contextlib from typing import Any, cast from datetime import datetime from anthropic import AsyncAnthropic from anthropic.lib.tools import beta_async_tool from anthropic.types.beta import BetaManagedAgentsCustomToolParams from anthropic.lib.environments import MANAGED_AGENTS_BETA from anthropic.lib.tools.agent_toolset import beta_agent_toolset_20260401 POLL_TIMEOUT_S = 60 MODEL_ID = "claude-haiku-4-5" # A custom tool. Because @beta_async_tool produces the same type that # client.beta.messages.tool_runner accepts, the worker can run it alongside the # built-in agent_toolset tools with no extra wiring. @beta_async_tool async def current_time() -> str: """Return the host's current local time as an ISO-8601 string.""" return datetime.now().isoformat() def _require_env(name: str) -> str: val = os.environ.get(name) if not val: sys.exit(f"error: environment variable {name} is required") return val async def main() -> None: logging.basicConfig(level=logging.INFO) client = AsyncAnthropic() environment_id = _require_env("ANTHROPIC_ENVIRONMENT_ID") environment_key = _require_env("ANTHROPIC_ENVIRONMENT_KEY") workdir = os.environ.get("ANTHROPIC_WORKDIR", ".") # 1. Create an agent that has both the built-in toolset and our custom tool. # The Agents API uses its own custom-tool TypedDict and rejects # `additionalProperties` (which pydantic emits), so derive a clean schema. schema = {k: v for k, v in dict(current_time.input_schema).items() if k != "additionalProperties"} custom_tool_param: BetaManagedAgentsCustomToolParams = { "type": "custom", "name": current_time.name, "description": current_time.description or "Return the host's current local time.", "input_schema": cast("Any", schema), } agent = await client.beta.agents.create( name="self-hosted-runner-example", model={"id": MODEL_ID}, system="You are a test agent running in a self-hosted sandbox. Use the available tools.", tools=[{"type": "agent_toolset_20260401"}, custom_tool_param], ) print(f"created agent {agent.id}") # 2. Create a session bound to the self-hosted environment. The # MANAGED_AGENTS_BETA header is required for the server to accept a # self-hosted environment_id on Sessions endpoints. session = await client.beta.sessions.create( agent=agent.id, environment_id=environment_id, title="self-hosted-runner-example", betas=[MANAGED_AGENTS_BETA], ) print(f"created session {session.id}") try: # 3. Send the user prompt that will trigger both a built-in tool (bash) # and the custom tool (current_time). await client.beta.sessions.events.send( session.id, events=[ { "type": "user.message", "content": [ { "type": "text", "text": "What is the current time? Also run pwd to show me the working directory.", } ], } ], betas=[MANAGED_AGENTS_BETA], ) # 4. Service the work locally: the worker polls for work, and for each # claimed session sets up the workdir + downloads the agent's skills, # runs the local tools against the session's tool calls while # heartbeating the work-item lease, then force-stops the work. The # `tools` factory binds the built-in toolset to the per-session # `AgentToolContext` and adds our custom tool. (Use # `client.beta.sessions.events.tool_runner(...)` directly if you want # to observe each dispatched tool call.) # # `client.beta.environments.work.worker(...)` builds an # `EnvironmentWorker`; you can also construct one directly with # `EnvironmentWorker(client, ...)` from `anthropic.lib.environments`. # # If you already hold a single claimed work item — e.g. an # `ant worker poll --on-work` script spawned this process for one # item — use `handle_item()` instead of `run()`. With no arguments it # serves the item described by the `ANTHROPIC_WORK_ID` / # `ANTHROPIC_ENVIRONMENT_ID` / `ANTHROPIC_SESSION_ID` / # `ANTHROPIC_ENVIRONMENT_KEY` env vars that command sets, and # `environment_id` isn't needed: # await client.beta.environments.work.worker(workdir=workdir, tools=...).handle_item() worker = client.beta.environments.work.worker( environment_id=environment_id, environment_key=environment_key, workdir=workdir, tools=lambda env: [*beta_agent_toolset_20260401(env), current_time], ) # The worker runs forever; bound it for the demo so the script exits # after the model has finished responding. with contextlib.suppress(asyncio.TimeoutError): await asyncio.wait_for(worker.run(), timeout=POLL_TIMEOUT_S) # 5. Print the resulting transcript. print("\n--- transcript ---") async for ev in client.beta.sessions.events.list(session.id, limit=100, betas=[MANAGED_AGENTS_BETA]): print(_summarise_event(ev)) finally: # 6. Clean up the session so the environment's work queue stays empty. await client.beta.sessions.delete(session.id, betas=[MANAGED_AGENTS_BETA]) print(f"\ndeleted session {session.id}") def _summarise_event(ev: Any) -> str: ev_type: str = getattr(ev, "type", "?") if ev_type == "agent.tool_use": return f"{ev_type}: name={getattr(ev, 'name', '?')} input={getattr(ev, 'input', {})!r}" content: Any = getattr(ev, "content", None) if not content: return ev_type first = content[0] text: str | None = getattr(first, "text", None) if text is None and hasattr(first, "get"): text = first.get("text") if not text: return ev_type snippet = text[:120] + ("..." if len(text) > 120 else "") return f"{ev_type}: {snippet}" if __name__ == "__main__": asyncio.run(main()) anthropic-sdk-python-0.120.2/examples/managed-agents-streaming-deltas-manual.py000066400000000000000000000071431523216435200275300ustar00rootroot00000000000000#!/usr/bin/env python3 """Streams a session with ``event_deltas`` enabled and folds the ``event_start`` / ``event_delta`` previews into ``agent.message`` snapshots with ``accumulate_managed_agents_event`` — for callers who want to own the preview lifecycle themselves. """ from __future__ import annotations import sys from anthropic import Anthropic from anthropic.lib.sessions import accumulate_managed_agents_event from anthropic.types.beta.sessions import BetaManagedAgentsAgentMessageEvent client = Anthropic() def main() -> None: # Create an environment, agent and session. environment = client.beta.environments.create( name="streaming-deltas-manual-example", ) print("Created environment:", environment.id) agent = client.beta.agents.create( name="streaming-deltas-manual-example", model="claude-sonnet-4-6", ) print("Created agent:", agent.id) session = client.beta.sessions.create( environment_id=environment.id, agent={"type": "agent", "id": agent.id}, ) print("Created session:", session.id) # Send a user message. client.beta.sessions.events.send( session.id, events=[ { "type": "user.message", "content": [{"type": "text", "text": "Write a short haiku about the ocean."}], } ], ) # Open the event stream with ``event_deltas`` enabled so ``agent.message`` # text arrives incrementally as ``event_start`` / ``event_delta`` previews # before the buffered final event. print("\nStreaming:") with client.beta.sessions.events.stream( session.id, event_deltas=["agent.message"], ) as stream: # One snapshot per previewed event id. previews: dict[str, BetaManagedAgentsAgentMessageEvent] = {} for ev in stream: event_id = ( ev.event_id if ev.type == "event_delta" else ev.event.id if ev.type == "event_start" else getattr(ev, "id", None) ) prev = previews.get(event_id) if event_id is not None else None if ev.type == "event_delta" and prev is None: # The preview was already closed (e.g. dropped below at # ``span.model_request_end``) — ignore the stray delta. continue preview = accumulate_managed_agents_event(prev, ev) if event_id is not None and preview is not None: previews[event_id] = preview if ev.type == "event_delta": if preview is not None and preview.type == "agent.message": text = "".join(b.text for b in preview.content) sys.stdout.write(f"\r{text}") sys.stdout.flush() elif ev.type == "agent.message": assert event_id is not None previews.pop(event_id, None) sys.stdout.write("\n") print("[final]", "".join(b.text for b in ev.content)) elif ev.type == "span.model_request_end": # The model request ended — any open preview will not get a buffered # event, so drop it. previews.clear() elif ev.type == "session.status_idle": # The session is no longer doing work (whatever the stop reason) # and the stream stays open, so stop reading. break elif ev.type == "session.error": print("[error]", ev.error.type, ev.error.message, file=sys.stderr) break main() anthropic-sdk-python-0.120.2/examples/managed-agents-worker-dispatch.py000066400000000000000000000063031523216435200261150ustar00rootroot00000000000000#!/usr/bin/env python3 """Service one already-claimed work item — the self-hosted "sandbox process" shape. Unlike ``managed-agents-self-hosted-sandbox-worker.py`` (which creates an agent + session and runs ``EnvironmentWorker.run()`` as a long-running poll loop), this process does *not* create anything and does *not* poll. Something upstream — an ``ant worker poll --on-work`` script, or your own orchestrator that spawns a sandbox per work item — already claimed a ``session`` work item and handed it to this process. Our only job is to run that one item's tool calls to completion, then exit. ``EnvironmentWorker.handle_item()`` with no arguments reads the work-item identity from the environment variables that ``ant worker poll --on-work`` sets on the process it spawns: ANTHROPIC_WORK_ID the claimed work item to service ANTHROPIC_ENVIRONMENT_ID the self-hosted environment it belongs to ANTHROPIC_SESSION_ID the session whose tool calls we run ANTHROPIC_ENVIRONMENT_KEY the environment key (the worker's single credential) It builds the per-session workdir, downloads the session agent's skills, runs the tools while heartbeating the work-item lease, and force-stops the item on exit. Security model: the worker executes bash and file operations directly on the host. Run inside a container or other isolation boundary you control. """ from __future__ import annotations import asyncio import logging from datetime import datetime from anthropic import AsyncAnthropic from anthropic.lib.tools import beta_async_tool from anthropic.lib.tools.agent_toolset import beta_agent_toolset_20260401 # A custom tool, same pattern as managed-agents-self-hosted-sandbox-worker.py: # @beta_async_tool produces the same type the worker's tool runner accepts, so it # runs alongside the built-in agent_toolset tools with no extra wiring. @beta_async_tool async def current_time() -> str: """Return the host's current local time as an ISO-8601 string.""" return datetime.now().isoformat() async def main() -> None: logging.basicConfig(level=logging.INFO) client = AsyncAnthropic() # No agent/session creation and no polling here — an upstream poller already # claimed the item. Build the worker with just a `tools` factory: it is # invoked once per claimed session with that session's `AgentToolContext`, # so the built-in toolset binds to the right per-session workdir. # (`worker(...)` returns an `EnvironmentWorker`; you can also construct one # directly with `EnvironmentWorker(client, ...)` from `anthropic.lib.environments`.) worker = client.beta.environments.work.worker( workdir="/workspace", tools=lambda env: [*beta_agent_toolset_20260401(env), current_time], ) # handle_item() with no arguments reads ANTHROPIC_WORK_ID / # ANTHROPIC_ENVIRONMENT_ID / ANTHROPIC_SESSION_ID / ANTHROPIC_ENVIRONMENT_KEY # from the environment, then runs the per-item flow: set up the workdir + # skills, run the session's tool calls while heartbeating the lease, and # force-stop the work item on exit. It returns once the item is done. await worker.handle_item() print("work item complete") if __name__ == "__main__": asyncio.run(main()) anthropic-sdk-python-0.120.2/examples/mcp_tool_runner.py000066400000000000000000000033271523216435200233460ustar00rootroot00000000000000"""Example showing how to use MCP helpers with tool_runner(). Connects to an MCP server, converts its tools to Anthropic-compatible tools using async_mcp_tool(), and runs them in a tool_runner() loop. Requires: pip install anthropic[mcp] Requires: Python 3.10+ """ import asyncio import rich from mcp import ClientSession from mcp.client.stdio import StdioServerParameters, stdio_client from anthropic import AsyncAnthropic from anthropic.lib.tools.mcp import async_mcp_tool client = AsyncAnthropic() async def main() -> None: # Connect to a local MCP server via stdio # This example uses the MCP filesystem server; replace with your own server server_params = StdioServerParameters( command="npx", args=["-y", "@modelcontextprotocol/server-filesystem", "/tmp"], ) async with stdio_client(server_params) as (read, write): async with ClientSession(read, write) as mcp_client: await mcp_client.initialize() # List available tools from the MCP server and convert them tools_result = await mcp_client.list_tools() tools = [async_mcp_tool(t, mcp_client) for t in tools_result.tools] print(f"Connected to MCP server with {len(tools)} tools:") for tool in tools: print(f" - {tool.name}") print() # Run a conversation with tool_runner() runner = client.beta.messages.tool_runner( model="claude-sonnet-5", max_tokens=1024, tools=tools, messages=[{"role": "user", "content": "List the files in /tmp"}], ) async for message in runner: rich.print(message) asyncio.run(main()) anthropic-sdk-python-0.120.2/examples/memory/000077500000000000000000000000001523216435200210725ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/examples/memory/basic.py000066400000000000000000000224541523216435200225340ustar00rootroot00000000000000import time import threading from typing import Optional from pydantic import TypeAdapter from anthropic import Anthropic from anthropic.tools import BetaLocalFilesystemMemoryTool from anthropic.types.beta import ( BetaMessageParam, BetaContentBlockParam, BetaMemoryTool20250818Command, BetaMemoryTool20250818ViewCommand, ) from anthropic.types.beta.beta_context_management_config_param import BetaContextManagementConfigParam # Context management automatically clears old tool results to stay within token limits # Triggers when input exceeds 30k tokens, keeps 3 tool uses after clearing DEFAULT_CONTEXT_MANAGEMENT: BetaContextManagementConfigParam = { "edits": [ { "type": "clear_tool_uses_20250919", # The below parameters are OPTIONAL: # Trigger clearing when threshold is exceeded "trigger": {"type": "input_tokens", "value": 30000}, # Number of tool uses to keep after clearing "keep": {"type": "tool_uses", "value": 3}, # Optional: Clear at least this many tokens "clear_at_least": {"type": "input_tokens", "value": 5000}, # Exclude these tools uses from being cleared "exclude_tools": ["web_search"], } ] } DEFAULT_MEMORY_SYSTEM_PROMPT = """- ***DO NOT just store the conversation history** - No need to mention your memory tool or what you are writing in it to the user, unless they ask - Store facts about the user and their preferences - Before responding, check memory to adjust technical depth and response style appropriately - Keep memories up-to-date - remove outdated info, add new details as you learn them - Use an xml format like John Doe""" class Spinner: def __init__(self, message: str = "Thinking"): self.message = message self.spinning = False self.thread = None def start(self): self.spinning = True self.thread = threading.Thread(target=self._spin) self.thread.start() def stop(self): self.spinning = False if self.thread: self.thread.join() print("\r" + " " * (len(self.message) + 10) + "\r", end="", flush=True) def _spin(self): chars = "⠋⠙⠹⠸⠼⠴⠦⠧⠇â " i = 0 while self.spinning: print(f"\r{self.message} {chars[i % len(chars)]}", end="", flush=True) i += 1 time.sleep(0.1) def conversation_loop(): client = Anthropic() memory = BetaLocalFilesystemMemoryTool() messages: list[BetaMessageParam] = [] # Initialize tracking for debug last_response_id: Optional[str] = None last_usage = None print("🧠 Claude with Memory & Web Search - Interactive Session") print("Commands:") print(" /quit or /exit - Exit the session") print(" /clear - Start fresh conversation") print(" /memory_view - See all memory files") print(" /memory_clear - Delete all memory") print(" /debug - View conversation history and token usage") # Display context management settings print(f"\n🧹 Context Management") print("=" * 60) while True: try: user_input = input("\nYou: ").strip() except (EOFError, KeyboardInterrupt): print("\nGoodbye!") break if user_input.lower() in ["/quit", "/exit"]: print("Goodbye!") break elif user_input.lower() == "/clear": messages = [] print("Conversation cleared!") continue elif user_input.lower() == "/memory_view": result = memory.execute(BetaMemoryTool20250818ViewCommand(command="view", path="/memories")) print("\n📠Memory contents:") print(result) continue elif user_input.lower() == "/memory_clear": result = memory.clear_all_memory() print(f"ðŸ—‘ï¸ {result}") continue elif user_input.lower() == "/debug": print("\n🔠Conversation history:") # Show last response ID if available if last_response_id: print(f"📌 Last response ID: {last_response_id}") # Show token usage if available if last_usage: usage = last_usage input_tokens = usage.get("input_tokens", 0) cached_tokens = usage.get("cache_read_input_tokens", 0) uncached_tokens = input_tokens - cached_tokens print(f"📊 Last API call tokens:") print(f" Total input: {input_tokens:,} tokens") print(f" Cached: {cached_tokens:,} tokens") print(f" Uncached: {uncached_tokens:,} tokens") threshold = DEFAULT_CONTEXT_MANAGEMENT["edits"][0]["trigger"]["value"] # type: ignore print(f" Context clearing threshold: {threshold:,} tokens") if input_tokens >= threshold: print(f" 🧹 Context clearing should trigger soon!") elif input_tokens >= threshold * 0.8: # 80% of threshold #type: ignore print(f" âš ï¸ Approaching context clearing threshold!") print("=" * 80) for i, message in enumerate(messages): role = message["role"].upper() print(f"\n[{i + 1}] {role}:") print("-" * 40) content = message["content"] if isinstance(content, str): print(content[:500] + "..." if len(content) > 500 else content) elif isinstance(content, list): for block in content: if isinstance(block, dict): if block.get("type") in ["tool_use", "server_tool_use"]: print(f"Tool: {block.get('name', 'unknown')}") elif block.get("type") == "tool_result": print(f"Tool Result: [content]") elif block.get("type") == "text": text = block.get("text", "") print(f"Text: {text[:200]}..." if len(text) > 200 else f"Text: {text}") print("=" * 80) continue elif not user_input: continue messages.append({"role": "user", "content": user_input}) print("\nClaude: ", end="", flush=True) spinner = Spinner("Thinking") spinner.start() # Use tool_runner with memory tool try: runner = client.beta.messages.tool_runner( betas=["context-management-2025-06-27"], model="claude-sonnet-5", max_tokens=2048, system=DEFAULT_MEMORY_SYSTEM_PROMPT, messages=messages, tools=[memory], context_management=DEFAULT_CONTEXT_MANAGEMENT, ) except Exception: spinner.stop() raise # Process all messages from the runner for message in runner: spinner.stop() # Store response ID and usage for debug display last_response_id = message.id if hasattr(message, "usage") and message.usage: last_usage = message.usage.model_dump() if hasattr(message.usage, "model_dump") else dict(message.usage) # Check for context management actions if message.context_management: for edit in message.context_management.applied_edits: print(f"\n🧹 [Context Management: {edit.type} applied]") # Process content blocks assistant_content: list[BetaContentBlockParam] = [] for content in message.content: if content.type == "text": print(content.text, end="", flush=True) assistant_content.append({"type": "text", "text": content.text}) elif content.type == "tool_use" and content.name == "memory": tool_input = TypeAdapter[BetaMemoryTool20250818Command]( BetaMemoryTool20250818Command ).validate_python(content.input) print(f"\n[Memory tool called: {tool_input.command}]") assistant_content.append( { "type": "tool_use", "id": content.id, "name": content.name, "input": content.input, } ) # Store assistant message if assistant_content: messages.append({"role": "assistant", "content": assistant_content}) # Generate tool response automatically tool_response = runner.generate_tool_call_response() if tool_response and tool_response["content"]: # Add tool results to messages messages.append({"role": "user", "content": tool_response["content"]}) for result in tool_response["content"]: if isinstance(result, dict) and result.get("type") == "tool_result": print(f"[Tool result processed]") print() if __name__ == "__main__": conversation_loop() anthropic-sdk-python-0.120.2/examples/messages.py000066400000000000000000000012201523216435200217360ustar00rootroot00000000000000from anthropic import Anthropic client = Anthropic() response = client.messages.create( max_tokens=1024, messages=[ { "role": "user", "content": "Hello!", } ], model="claude-sonnet-5", ) print(response) response2 = client.messages.create( max_tokens=1024, messages=[ { "role": "user", "content": "Hello!", }, { "role": response.role, "content": response.content, }, { "role": "user", "content": "How are you?", }, ], model="claude-sonnet-5", ) print(response2) anthropic-sdk-python-0.120.2/examples/messages_stream.py000077500000000000000000000017471523216435200233320ustar00rootroot00000000000000#!/usr/bin/env -S rye run python import asyncio from anthropic import AsyncAnthropic client = AsyncAnthropic() async def main() -> None: async with client.messages.stream( max_tokens=1024, messages=[ { "role": "user", "content": "Say hello there!", } ], model="claude-sonnet-5", ) as stream: async for event in stream: if event.type == "text": print(event.text, end="", flush=True) elif event.type == "content_block_stop": print() print("\ncontent block finished accumulating:", event.content_block) print() # you can still get the accumulated final message outside of # the context manager, as long as the entire stream was consumed # inside of the context manager accumulated = await stream.get_final_message() print("accumulated message: ", accumulated.to_json()) asyncio.run(main()) anthropic-sdk-python-0.120.2/examples/structured_outputs.py000066400000000000000000000011121523216435200241360ustar00rootroot00000000000000import pydantic import anthropic class Order(pydantic.BaseModel): product_name: str price: float quantity: int client = anthropic.Anthropic() prompt = """ Extract the product name, price, and quantity from this customer message: "Hi, I’d like to order 2 packs of Green Tea for 5.50 dollars each." """ parsed_message = client.messages.parse( model="claude-sonnet-5", messages=[{"role": "user", "content": prompt}], max_tokens=1024, output_format=Order, ) print(parsed_message.parsed_output) # Order(product_name='Green Tea', price=5.5, quantity=2) anthropic-sdk-python-0.120.2/examples/structured_outputs_streaming.py000066400000000000000000000013421523216435200262140ustar00rootroot00000000000000import pydantic import anthropic class Order(pydantic.BaseModel): product_name: str price: float quantity: int client = anthropic.Anthropic() prompt = """ Extract the product name, price, and quantity from this customer message: "Hi, I'd like to order 2 packs of Green Tea for 5.50 dollars each." """ with client.messages.stream( model="claude-sonnet-5", messages=[{"role": "user", "content": prompt}], max_tokens=1024, output_format=Order, ) as stream: for event in stream: if event.type == "text": print(event.parsed_snapshot()) # Get the final parsed output final_message = stream.get_final_message() print(f"\nFinal parsed order: {final_message.parsed_output}") anthropic-sdk-python-0.120.2/examples/text_completions_demo_async.py000066400000000000000000000006711523216435200257410ustar00rootroot00000000000000#!/usr/bin/env -S uv run python import asyncio import anthropic from anthropic import AsyncAnthropic async def main() -> None: client = AsyncAnthropic() res = await client.completions.create( model="claude-sonnet-5", prompt=f"{anthropic.HUMAN_PROMPT} how does a court case get to the Supreme Court? {anthropic.AI_PROMPT}", max_tokens_to_sample=1000, ) print(res.completion) asyncio.run(main()) anthropic-sdk-python-0.120.2/examples/text_completions_demo_sync.py000066400000000000000000000006061523216435200255760ustar00rootroot00000000000000#!/usr/bin/env -S uv run python import anthropic from anthropic import Anthropic def main() -> None: client = Anthropic() res = client.completions.create( model="claude-sonnet-5", prompt=f"{anthropic.HUMAN_PROMPT} how does a court case get to the Supreme Court? {anthropic.AI_PROMPT}", max_tokens_to_sample=1000, ) print(res.completion) main() anthropic-sdk-python-0.120.2/examples/text_completions_streaming.py000066400000000000000000000025401523216435200256060ustar00rootroot00000000000000#!/usr/bin/env -S uv run python import asyncio from anthropic import AI_PROMPT, HUMAN_PROMPT, Anthropic, APIStatusError, AsyncAnthropic client = Anthropic() async_client = AsyncAnthropic() question = """ Hey Claude! How can I recursively list all files in a directory in Python? """ def sync_stream() -> None: stream = client.completions.create( prompt=f"{HUMAN_PROMPT} {question}{AI_PROMPT}", model="claude-sonnet-5", stream=True, max_tokens_to_sample=300, ) for completion in stream: print(completion.completion, end="", flush=True) print() async def async_stream() -> None: stream = await async_client.completions.create( prompt=f"{HUMAN_PROMPT} {question}{AI_PROMPT}", model="claude-sonnet-5", stream=True, max_tokens_to_sample=300, ) async for completion in stream: print(completion.completion, end="", flush=True) print() def stream_error() -> None: try: client.completions.create( prompt=f"{HUMAN_PROMPT} {question}{AI_PROMPT}", model="claude-unknown-model", stream=True, max_tokens_to_sample=300, ) except APIStatusError as err: print(f"Caught API status error with response body: {err.response.text}") sync_stream() asyncio.run(async_stream()) stream_error() anthropic-sdk-python-0.120.2/examples/thinking.py000066400000000000000000000007031523216435200217470ustar00rootroot00000000000000import anthropic client = anthropic.Anthropic() response = client.messages.create( model="claude-sonnet-5", max_tokens=3200, thinking={"type": "enabled", "budget_tokens": 1600}, messages=[{"role": "user", "content": "Create a haiku about Anthropic."}], ) for block in response.content: if block.type == "thinking": print(f"Thinking: {block.thinking}") elif block.type == "text": print(f"Text: {block.text}") anthropic-sdk-python-0.120.2/examples/thinking_stream.py000066400000000000000000000013631523216435200233250ustar00rootroot00000000000000import anthropic client = anthropic.Anthropic() with client.messages.stream( model="claude-sonnet-5", max_tokens=3200, thinking={"type": "enabled", "budget_tokens": 1600}, messages=[{"role": "user", "content": "Create a haiku about Anthropic."}], ) as stream: thinking = "not-started" for event in stream: if event.type == "thinking": if thinking == "not-started": print("Thinking:\n---------") thinking = "started" print(event.thinking, end="", flush=True) elif event.type == "text": if thinking != "finished": print("\n\nText:\n-----") thinking = "finished" print(event.text, end="", flush=True) anthropic-sdk-python-0.120.2/examples/tools.py000066400000000000000000000025401523216435200212750ustar00rootroot00000000000000from __future__ import annotations from anthropic import Anthropic from anthropic.types import ToolParam, MessageParam client = Anthropic() user_message: MessageParam = { "role": "user", "content": "What is the weather in SF?", } tools: list[ToolParam] = [ { "name": "get_weather", "description": "Get the weather for a specific location", "input_schema": { "type": "object", "properties": {"location": {"type": "string"}}, }, } ] message = client.messages.create( model="claude-sonnet-5", max_tokens=1024, messages=[user_message], tools=tools, ) print(f"Initial response: {message.model_dump_json(indent=2)}") assert message.stop_reason == "tool_use" tool = next(c for c in message.content if c.type == "tool_use") response = client.messages.create( model="claude-sonnet-5", max_tokens=1024, messages=[ user_message, {"role": message.role, "content": message.content}, { "role": "user", "content": [ { "type": "tool_result", "tool_use_id": tool.id, "content": [{"type": "text", "text": "The weather is 73f"}], } ], }, ], tools=tools, ) print(f"\nFinal response: {response.model_dump_json(indent=2)}") anthropic-sdk-python-0.120.2/examples/tools_runner.py000066400000000000000000000027671523216435200227010ustar00rootroot00000000000000import json from typing_extensions import Literal import rich from anthropic import Anthropic, beta_tool client = Anthropic() @beta_tool def get_weather(location: str, units: Literal["c", "f"]) -> str: """Lookup the weather for a given city in either celsius or fahrenheit Args: location: The city and state, e.g. San Francisco, CA units: Unit for the output, either 'c' for celsius or 'f' for fahrenheit Returns: A dictionary containing the location, temperature, and weather condition. """ # Simulate a weather API call print(f"Fetching weather for {location} in {units}") # Here you would typically make an API call to a weather service # For demonstration, we return a mock response if units == "c": return json.dumps( { "location": location, "temperature": "20°C", "condition": "Sunny", } ) else: return json.dumps( { "location": location, "temperature": "68°F", "condition": "Sunny", } ) def main() -> None: runner = client.beta.messages.tool_runner( max_tokens=1024, model="claude-sonnet-5", # alternatively, you can use `tools=[anthropic.beta_tool(get_weather)]` tools=[get_weather], messages=[{"role": "user", "content": "What is the weather in SF?"}], ) for message in runner: rich.print(message) main() anthropic-sdk-python-0.120.2/examples/tools_runner_search_tool.py000066400000000000000000000045471523216435200252610ustar00rootroot00000000000000import json from typing import Any, List from typing_extensions import Literal import rich from anthropic import Anthropic, beta_tool from anthropic.lib.tools import BetaFunctionTool, BetaFunctionToolResultType from anthropic.types.beta import BetaToolReferenceBlockParam client = Anthropic() @beta_tool(defer_loading=True) def get_weather(location: str, units: Literal["c", "f"]) -> str: """Lookup the weather for a given city in either celsius or fahrenheit Args: location: The city and state, e.g. San Francisco, CA units: Unit for the output, either 'c' for celsius or 'f' for fahrenheit Returns: A dictionary containing the location, temperature, and weather condition. """ # Simulate a weather API call print(f"Fetching weather for {location} in {units}") # Here you would typically make an API call to a weather service # For demonstration, we return a mock response if units == "c": return json.dumps( { "location": location, "temperature": "20°C", "condition": "Sunny", } ) else: return json.dumps( { "location": location, "temperature": "68°F", "condition": "Sunny", } ) def make_tool_searcher(tools: List[BetaFunctionTool[Any]]) -> BetaFunctionTool[Any]: """Returns a tool that Claude can use to search through all available tools""" @beta_tool def search_available_tools(*, keyword: str) -> BetaFunctionToolResultType: """Search for useful tools using a query string""" results: list[BetaToolReferenceBlockParam] = [] for tool in tools: if keyword in json.dumps(tool.to_dict()): results.append({"type": "tool_reference", "tool_name": tool.name}) return results return search_available_tools def main() -> None: tools: list[BetaFunctionTool[Any]] = [ get_weather, # ... many more tools ] runner = client.beta.messages.tool_runner( max_tokens=1024, model="claude-sonnet-5", tools=[*tools, make_tool_searcher(tools)], messages=[{"role": "user", "content": "What is the weather in SF?"}], betas=["tool-search-tool-2025-10-19"], ) for message in runner: rich.print(message) main() anthropic-sdk-python-0.120.2/examples/tools_stream.py000066400000000000000000000023321523216435200226470ustar00rootroot00000000000000import asyncio from anthropic import AsyncAnthropic client = AsyncAnthropic() async def main() -> None: async with client.messages.stream( max_tokens=1024, model="claude-sonnet-5", tools=[ { "name": "get_weather", "description": "Get the weather at a specific location", "input_schema": { "type": "object", "properties": { "location": {"type": "string", "description": "The city and state, e.g. San Francisco, CA"}, "unit": { "type": "string", "enum": ["celsius", "fahrenheit"], "description": "Unit for the output", }, }, "required": ["location"], }, } ], messages=[{"role": "user", "content": "What is the weather in SF?"}], ) as stream: async for event in stream: if event.type == "input_json": print(f"delta: {repr(event.partial_json)}") print(f"snapshot: {event.snapshot}") print() asyncio.run(main()) anthropic-sdk-python-0.120.2/examples/vertex.py000066400000000000000000000015521523216435200214540ustar00rootroot00000000000000import asyncio from anthropic import AnthropicVertex, AsyncAnthropicVertex def sync_client() -> None: print("------ Sync Vertex ------") client = AnthropicVertex() message = client.messages.create( model="claude-sonnet-4@20250514", max_tokens=100, messages=[ { "role": "user", "content": "Hello!", } ], ) print(message.to_json()) async def async_client() -> None: print("------ Async Vertex ------") client = AsyncAnthropicVertex() message = await client.messages.create( model="claude-sonnet-4@20250514", max_tokens=1024, messages=[ { "role": "user", "content": "Hello!", } ], ) print(message.to_json()) sync_client() asyncio.run(async_client()) anthropic-sdk-python-0.120.2/examples/web_search.py000066400000000000000000000017051523216435200222410ustar00rootroot00000000000000from __future__ import annotations from anthropic import Anthropic client = Anthropic() # Create a message with web search enabled message = client.messages.create( model="claude-sonnet-5", max_tokens=1024, messages=[{"role": "user", "content": "What's the weather in New York?"}], tools=[ { "name": "web_search", "type": "web_search_20250305", } ], ) # Print the full response print("\nFull response:") print(message.model_dump_json(indent=2)) # Extract and print the content print("\nResponse content:") for content_block in message.content: if content_block.type == "text": print(content_block.text) # Print usage information print("\nUsage statistics:") print(f"Input tokens: {message.usage.input_tokens}") print(f"Output tokens: {message.usage.output_tokens}") if message.usage.server_tool_use: print(f"Web search requests: {message.usage.server_tool_use.web_search_requests}") anthropic-sdk-python-0.120.2/examples/web_search_stream.py000066400000000000000000000042741523216435200236200ustar00rootroot00000000000000import asyncio from anthropic import AsyncAnthropic async def main() -> None: client = AsyncAnthropic() print("Claude with Web Search (Streaming)") print("==================================") # Create an async stream with web search enabled async with client.beta.messages.stream( model="claude-sonnet-5", max_tokens=1024, messages=[{"role": "user", "content": "What's the weather in New York?"}], tools=[ { "name": "web_search", "type": "web_search_20250305", } ], ) as stream: # Process streaming events async for chunk in stream: # Print text deltas as they arrive if chunk.type == "content_block_delta" and chunk.delta.type == "text_delta": print(chunk.delta.text, end="", flush=True) # Track when web search is being used elif chunk.type == "content_block_start" and chunk.content_block.type == "web_search_tool_result": print("\n[Web search started...]", end="", flush=True) elif chunk.type == "content_block_stop" and chunk.content_block.type == "web_search_tool_result": print("[Web search completed]", end="\n\n", flush=True) # Get the final complete message message = await stream.get_final_message() print("\n\nFinal usage statistics:") print(f"Input tokens: {message.usage.input_tokens}") print(f"Output tokens: {message.usage.output_tokens}") if message.usage.server_tool_use: print(f"Web search requests: {message.usage.server_tool_use.web_search_requests}") else: print("No web search requests recorded in usage") # Rather than parsing the web search results structure (which varies), # we'll just show the complete message structure for debugging print("\nMessage Content Types:") for i, block in enumerate(message.content): print(f"Content Block {i + 1}: Type = {block.type}") # Show the entire message structure as JSON for debugging print("\nComplete message structure (JSON):") print(message.model_dump_json(indent=2)) if __name__ == "__main__": asyncio.run(main()) anthropic-sdk-python-0.120.2/examples/workload_identity.py000077500000000000000000000162621523216435200237010ustar00rootroot00000000000000#!/usr/bin/env python3 """ Workload Identity Federation & Credential Providers — comprehensive examples. The Anthropic client resolves auth in this precedence order: 1. Constructor args: api_key=, auth_token=, or credentials= 2. Env vars: ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN, then the credential chain below 3. Standard defaults: ~/.config/anthropic/credentials.json if it exists The `credentials=` arg accepts any AccessTokenProvider — a zero-arg callable returning AccessToken(token, expires_at). The client caches the token and proactively refreshes it (120s advisory / 30s mandatory before expiry). """ import os import anthropic from anthropic import ( AccessToken, StaticToken, CredentialsFile, IdentityTokenFile, WorkloadIdentityCredentials, ) # ============================================================================= # Section 1: Zero-config (recommended for production workloads) # ============================================================================= # Just construct the client. Auth is resolved from the environment. # # For Kubernetes / GitHub Actions / etc., set these env vars on the workload: # ANTHROPIC_IDENTITY_TOKEN_FILE=/var/run/secrets/kubernetes.io/serviceaccount/token # ANTHROPIC_FEDERATION_RULE_ID=fdrl_01... # ANTHROPIC_ORGANIZATION_ID=00000000-0000-0000-0000-000000000000 # ANTHROPIC_SERVICE_ACCOUNT_ID=svac_01... (optional) # # Or, if a sidecar/daemon writes a profile config + credentials file: # ANTHROPIC_PROFILE=my-profile (picks ~/.config/anthropic/configs/my-profile.json # and ~/.config/anthropic/credentials/my-profile.json) # ANTHROPIC_CONFIG_DIR=/etc/anthropic (relocates the root; optional) # # Or, the existing env vars still work: # ANTHROPIC_API_KEY=sk-ant-... # ANTHROPIC_AUTH_TOKEN=sk-ant-oat01-... client = anthropic.Anthropic() # ============================================================================= # Section 2: Explicit credentials= via constructor # ============================================================================= # --- 2a. WorkloadIdentityCredentials: exchange an external OIDC JWT -------- # JWT source option i: read from a file (re-read on every refresh — handles k8s rotation) client = anthropic.Anthropic( credentials=WorkloadIdentityCredentials( identity_token_provider=IdentityTokenFile( "/var/run/secrets/kubernetes.io/serviceaccount/token", ), federation_rule_id="fdrl_01...", organization_id="00000000-0000-0000-0000-000000000000", service_account_id="svac_01...", ), ) # JWT source option ii: from an env var (CI systems that inject the token directly) client = anthropic.Anthropic( credentials=WorkloadIdentityCredentials( identity_token_provider=lambda: os.environ["ANTHROPIC_IDENTITY_TOKEN"], federation_rule_id="fdrl_01...", organization_id="00000000-0000-0000-0000-000000000000", ), ) # JWT source option iii: custom callable (secrets manager, internal token service, etc.) def fetch_jwt_from_vault() -> str: # your logic here return "" client = anthropic.Anthropic( credentials=WorkloadIdentityCredentials( identity_token_provider=fetch_jwt_from_vault, federation_rule_id="fdrl_01...", organization_id="00000000-0000-0000-0000-000000000000", ), ) # --- 2b. CredentialsFile: read auth config from a named profile on disk ---- # # Profiles live under the config directory (default ~/.config/anthropic/, # override with ANTHROPIC_CONFIG_DIR) as a pair of files: # # configs/.json — non-secret. Shape: # # { # "authentication": {"type": "oidc_federation"|"user_oauth", ...}, # "organization_id": "00000000-0000-0000-0000-000000000000", # "workspace_id": "wrkspc_01...", # "base_url": "https://api.anthropic.com" # } # # The "authentication" object is a tagged union discriminated on "type": # # {"type": "oidc_federation", # "federation_rule_id": "fdrl_...", # "service_account_id": "svac_...", # "identity_token": {"source": "file", "path": "..."}} # → SDK performs the jwt-bearer exchange itself. If "identity_token" # is omitted, ANTHROPIC_IDENTITY_TOKEN_FILE is used instead. # organization_id is read from the top level of the config. # # {"type": "user_oauth", "client_id": "..."} # → interactive PKCE login with refresh_token rotation. On access-token # expiry the SDK performs a refresh_token grant against # /v1/oauth/token and writes the new tokens back to # credentials/.json (atomic replace). # # {"type": "user_oauth"} (no client_id) # → credentials file is externally rotated by a sidecar/daemon. The # SDK re-reads the file on every refresh and returns whatever # access_token is there; no refresh grant is attempted. # # credentials/.json — secret (0600). Holds access_token, # expires_at, and (for user_oauth with # a client_id) refresh_token. # Point at a specific profile name: client = anthropic.Anthropic(credentials=CredentialsFile(profile="production")) # Or resolve the profile from ANTHROPIC_PROFILE / /active_config / "default": client = anthropic.Anthropic(credentials=CredentialsFile()) # --- 2c. StaticToken: you already have a bearer token --------------------- client = anthropic.Anthropic(credentials=StaticToken("sk-ant-oat01-...")) # (equivalent to anthropic.Anthropic(auth_token="sk-ant-oat01-...")) # --- 2d. Custom AccessTokenProvider --------------------------------------- # Any callable matching AccessTokenProvider works. The optional `force_refresh` # kwarg is set after a 401 retry; providers without a cache can ignore it. def my_provider(*, force_refresh: bool = False) -> AccessToken: # noqa: ARG001 # call your internal auth service here return AccessToken(token="sk-ant-oat01-...", expires_at=1775000000) client = anthropic.Anthropic(credentials=my_provider) # ============================================================================= # Section 3: Precedence demonstration # ============================================================================= # Constructor args always win over env vars, which win over default file paths. # Within constructor args, passing more than one of api_key/auth_token/credentials # is supported but credentials takes the Bearer slot (api_key still sends X-Api-Key # if both are set — generally don't do this). # ============================================================================= # Section 4: Async # ============================================================================= async def main() -> None: aclient = anthropic.AsyncAnthropic( credentials=WorkloadIdentityCredentials( identity_token_provider=IdentityTokenFile(), # uses ANTHROPIC_IDENTITY_TOKEN_FILE federation_rule_id="fdrl_01...", organization_id="00000000-0000-0000-0000-000000000000", ), ) msg = await aclient.messages.create( model="claude-opus-4-5", max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], ) print(msg) # asyncio.run(main()) anthropic-sdk-python-0.120.2/helpers.md000066400000000000000000000325341523216435200177370ustar00rootroot00000000000000# Message Helpers ## Streaming Responses ```python async with client.messages.stream( max_tokens=1024, messages=[ { "role": "user", "content": "Say hello there!", } ], model="claude-sonnet-5", ) as stream: async for text in stream.text_stream: print(text, end="", flush=True) print() ``` `client.messages.stream()` returns a `MessageStreamManager`, which is a context manager that yields a `MessageStream` which is iterable, emits events and accumulates messages. Alternatively, you can use `client.messages.create(..., stream=True)` which returns an iterable of the events in the stream and uses less memory (most notably, it does not accumulate a final message object for you). The stream will be cancelled when the context manager exits but you can also close it prematurely by calling `stream.close()`. See an example of streaming helpers in action in [`examples/messages_stream.py`](examples/messages_stream.py). > [!NOTE] > The synchronous client has the same interface just without `async/await`. ### Lenses #### `.text_stream` Provides an iterator over just the text deltas in the stream: ```py async for text in stream.text_stream: print(text, end="", flush=True) print() ``` ### Events The events listed here are just the event types that the SDK extends, for a full list of the events returned by the API, see [these docs](https://docs.anthropic.com/en/api/messages-streaming#event-types). ```py from anthropic import AsyncAnthropic client = AsyncAnthropic() async with client.messages.stream( max_tokens=1024, messages=[ { "role": "user", "content": "Say hello there!", } ], model="claude-sonnet-5", ) as stream: async for event in stream: if event.type == "text": print(event.text, end="", flush=True) elif event.type == 'content_block_stop': print('\n\ncontent block finished accumulating:', event.content_block) print() # you can still get the accumulated final message outside of # the context manager, as long as the entire stream was consumed # inside of the context manager accumulated = await stream.get_final_message() print("accumulated message: ", accumulated.to_json()) ``` #### `text` This event is yielded whenever a text `content_block_delta` event is returned by the API & includes the delta and the accumulated snapshot, e.g. ```py if event.type == "text": event.text # " there" event.snapshot # "Hello, there" ``` #### `input_json` This event is yielded whenever a JSON `content_block_delta` event is returned by the API & includes the delta and the accumulated snapshot, e.g. ```py if event.type == "input_json": event.partial_json # ' there"' event.snapshot # '{"message": "Hello, there"' ``` #### `message_stop` The event is fired when a full Message object has been accumulated. ```py if event.type == "message_stop": event.message # Message ``` #### `content_block_stop` The event is fired when a full ContentBlock object has been accumulated. ```py if event.type == "content_block_stop": event.content_block # ContentBlock ``` ### Methods #### `await .close()` Aborts the request. #### `await .until_done()` Blocks until the stream has been read to completion. #### `await .get_final_message()` Blocks until the stream has been read to completion and returns the accumulated `Message` object. #### `await .get_final_text()` > [!NOTE] > Currently the API will only ever return 1 content block Blocks until the stream has been read to completion and returns all `text` content blocks concatenated together. ## MCP Helpers This SDK provides helpers for integrating with [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) servers. These helpers convert MCP types to Anthropic API types, reducing boilerplate when working with MCP tools, prompts, and resources. > **Note:** The Claude API also supports an [`mcp_servers` parameter](https://docs.anthropic.com/en/docs/agents-and-tools/mcp) that lets Claude connect directly to remote MCP servers. > > - Use `mcp_servers` when you have remote servers accessible via URL and only need tool support. > - Use the MCP helpers when you need local MCP servers, prompts, resources, or more control over the MCP connection. > **Requires:** `pip install anthropic[mcp]` (Python 3.10+) ### Using MCP tools with tool_runner ```py from anthropic import AsyncAnthropic from anthropic.lib.tools.mcp import async_mcp_tool from mcp import ClientSession from mcp.client.stdio import stdio_client, StdioServerParameters client = AsyncAnthropic() async with stdio_client(StdioServerParameters(command="mcp-server")) as (read, write): async with ClientSession(read, write) as mcp_client: await mcp_client.initialize() tools_result = await mcp_client.list_tools() runner = await client.beta.messages.tool_runner( model="claude-sonnet-5", max_tokens=1024, messages=[{"role": "user", "content": "Use the available tools"}], tools=[async_mcp_tool(t, mcp_client) for t in tools_result.tools], ) async for message in runner: print(message) ``` > [!TIP] > If you're using the sync client, replace `async_mcp_tool` with `mcp_tool`. ### Using MCP prompts ```py from anthropic.lib.tools.mcp import mcp_message prompt = await mcp_client.get_prompt(name="my-prompt") response = await client.beta.messages.create( model="claude-sonnet-5", max_tokens=1024, messages=[mcp_message(m) for m in prompt.messages], ) ``` ### Using MCP resources as content ```py from anthropic.lib.tools.mcp import mcp_resource_to_content resource = await mcp_client.read_resource(uri="file:///path/to/doc.txt") response = await client.beta.messages.create( model="claude-sonnet-5", max_tokens=1024, messages=[{ "role": "user", "content": [ mcp_resource_to_content(resource), {"type": "text", "text": "Summarize this document"}, ], }], ) ``` ### Uploading MCP resources as files ```py from anthropic.lib.tools.mcp import mcp_resource_to_file resource = await mcp_client.read_resource(uri="file:///path/to/data.json") uploaded = await client.beta.files.upload(file=mcp_resource_to_file(resource)) ``` ### Error handling The conversion functions raise `UnsupportedMCPValueError` if an MCP value cannot be converted to a format supported by the Claude API (e.g., unsupported content type like audio, unsupported MIME type). # Self-Hosted Environment Runner For running a managed agent's tools locally against a self-hosted environment, the SDK exposes three pieces: - `client.beta.environments.work.worker(...)` — the full worker (an `EnvironmentWorker`; also constructible directly as `EnvironmentWorker(client, ...)` from `anthropic.lib.environments`): polls the environment for work, and for each claimed session sets up the workdir + downloads the session agent's skills, runs your tools against the session's `agent.tool_use` / `agent.custom_tool_use` events while heartbeating the work-item lease, force-stops the work on exit, and loops. `worker.handle_item(...)` runs that same per-item flow for a single work item you've already claimed; with no arguments it reads the work id / environment id / session id from `ANTHROPIC_WORK_ID` / `ANTHROPIC_ENVIRONMENT_ID` / `ANTHROPIC_SESSION_ID` and the environment key from `ANTHROPIC_ENVIRONMENT_KEY` (the env vars `ant worker poll --on-work` sets). `environment_id` passed to `worker()` is only needed by `run()`'s poll loop; `environment_key` is the worker's single credential — `handle_item()` falls back to the value passed to `worker()` and then to `ANTHROPIC_ENVIRONMENT_KEY`. Async only; built on `anyio`, so it works under either `asyncio` or `trio`. - `client.beta.sessions.events.tool_runner(...)` — the sessions-side counterpart to `client.beta.messages.tool_runner`: a `SessionToolRunner`, an async iterable that attaches to a session's event stream, runs the matching tool for each tool-call event — `agent.tool_use` (built-in tools) answered with `user.tool_result`, and `agent.custom_tool_use` (custom tools) answered with `user.custom_tool_result` — posts the result back, and yields one `DispatchedToolCall` per completed call. Use it directly when you want to observe each dispatch (the worker drives one internally). Async only. - `client.beta.environments.work.poller(...)` — the control-plane only piece: claims work items, ack's each one, and yields each claimed work item. Async only — available on `AsyncAnthropic` (its `worker(...)` companion is async too, so the sync client does not expose either). The standard `agent_toolset_20260401` implementations (`bash`, `read`, `write`, `edit`, `glob`, `grep`) plus the workdir/skills `AgentToolContext` live in `anthropic.lib.tools.agent_toolset`. The high-level worker is one object: ```python import os, asyncio from anthropic import AsyncAnthropic from anthropic.lib.tools import beta_async_tool from anthropic.lib.tools.agent_toolset import beta_agent_toolset_20260401 client = AsyncAnthropic() @beta_async_tool async def deploy(target: str) -> str: ... # `client.beta.environments.work.worker(...)` builds an `EnvironmentWorker`; you can also construct # one directly with `EnvironmentWorker(client, ...)` from `anthropic.lib.environments`. await client.beta.environments.work.worker( environment_id=os.environ["ANTHROPIC_ENVIRONMENT_ID"], environment_key=os.environ["ANTHROPIC_ENVIRONMENT_KEY"], workdir="/workspace", # `tools` is a fixed list or a factory invoked per session with that session's `AgentToolContext` # (use the factory form to bind `beta_agent_toolset_20260401` to the right session). Defaults to # `beta_agent_toolset_20260401(env)`. tools=lambda env: [*beta_agent_toolset_20260401(env), deploy], ).run() # loops forever; cancel the task / wrap in asyncio.wait_for to bound it ``` If you already hold a claimed work item — e.g. an `ant worker poll --on-work` script handed one to a fresh process — call `handle_item` to run just the per-item flow (build the workdir + skills, run the session's tools while heartbeating the lease, force-stop on exit). Inside that command the work id / environment id / session id / environment key are already in the environment, so the sandbox case is just: ```python await client.beta.environments.work.worker(workdir="/workspace", tools=tools).handle_item() ``` Pass the values explicitly when you have the objects in hand (e.g. you iterate the poller yourself): ```python await client.beta.environments.work.worker(workdir="/workspace", tools=tools).handle_item( work_id=work.id, environment_id=work.environment_id, session_id=work.data.id, environment_key=environment_key, ) ``` If you want to observe each tool call (or wire up the workdir / poller yourself), use the session tool runner directly: ```python from anthropic import AsyncAnthropic from anthropic.lib.tools.agent_toolset import AgentToolContext, beta_agent_toolset_20260401 client = AsyncAnthropic() async for work in client.beta.environments.work.poller( environment_id=..., environment_key=environment_key, ): if work.data.type != "session": continue # Passing `client` and `session_id` makes `AgentToolContext` fetch the session's resolved agent # on enter and download each of its skills into `{workdir}/skills//`. async with AgentToolContext(workdir="/workspace", client=client, session_id=work.data.id) as env: async for call in client.beta.sessions.events.tool_runner( work.data.id, tools=beta_agent_toolset_20260401(env), environment_key=environment_key, ): print(f"{call.name} -> {'error' if call.is_error else 'ok'}") ``` `beta_agent_toolset_20260401(env)` returns a plain `list[BetaAsyncFunctionTool]`. Filter or extend it directly: ```python from anthropic.lib.tools import beta_async_tool from anthropic.lib.tools.agent_toolset import beta_read_tool, beta_agent_toolset_20260401 tools = [*beta_agent_toolset_20260401(env), deploy] tools = [t for t in beta_agent_toolset_20260401(env) if t.name != "bash"] ``` > **Run stateful tools under the session tool runner, not the Messages tool runner.** The `bash` > tool owns a persistent `/bin/bash` subprocess that is only torn down by its `close` cleanup hook. > Only `client.beta.sessions.events.tool_runner(...)` (the `SessionToolRunner`) and the > `EnvironmentWorker` built on it call that hook. `client.beta.messages.tool_runner(...)` does > **not** call `close`, so handing it this toolset leaks one orphaned shell per run. Use the > session tool runner / environment worker for the agent toolset, or drop `bash` (as in the second > line above) before passing the toolset to the Messages tool runner. The `bash` tool runs an unrestricted `/bin/bash` and executes file operations and shell commands directly on the host. Run the worker inside a container or other isolation boundary you control. (The file tools — `read`/`write`/`edit`/`glob`/`grep` — confine to the workdir with a symlink-aware check, so they are safe without a sandbox; `bash` is not.) `bash` does not inherit the runner's `ANTHROPIC_*` credentials; pass `AgentToolContext(env=...)` to control the subprocess environment. See [`examples/managed-agents-self-hosted-sandbox-worker.py`](examples/managed-agents-self-hosted-sandbox-worker.py) for a complete example. anthropic-sdk-python-0.120.2/pyproject.toml000066400000000000000000000162151523216435200206650ustar00rootroot00000000000000[project] name = "anthropic" version = "0.120.2" description = "The official Python library for the anthropic API" dynamic = ["readme"] license = "MIT" authors = [ { name = "Anthropic", email = "support@anthropic.com" }, ] dependencies = [ "httpx>=0.25.0, <1", "pydantic>=1.9.0, <3", "typing-extensions>=4.14, <5", "anyio>=3.5.0, <5", "distro>=1.7.0, <2", "sniffio", "jiter>=0.4.0, <1", "docstring-parser>=0.15,<1", ] requires-python = ">= 3.9" classifiers = [ "Typing :: Typed", "Intended Audience :: Developers", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Operating System :: OS Independent", "Operating System :: POSIX", "Operating System :: MacOS", "Operating System :: POSIX :: Linux", "Operating System :: Microsoft :: Windows", "Topic :: Software Development :: Libraries :: Python Modules", "License :: OSI Approved :: MIT License" ] [project.optional-dependencies] aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.9, <1"] vertex = ["google-auth[requests] >=2, <3"] google_cloud = ["google-auth[requests] >=2, <3"] aws = ["boto3 >= 1.28.57", "botocore >= 1.31.57"] bedrock = ["boto3 >= 1.28.57", "botocore >= 1.31.57"] mcp = ["mcp>=1.0, <3; python_version >= '3.10'"] webhooks = ["standardwebhooks >= 1.0.1, < 2"] [project.urls] Homepage = "https://github.com/anthropics/anthropic-sdk-python" Repository = "https://github.com/anthropics/anthropic-sdk-python" [tool.uv] managed = true required-version = ">=0.9" # Ensure the lockfile always uses public PyPI, regardless of contributor's global uv config index = [{ url = "https://pypi.org/simple", default = true }] conflicts = [ [ { group = "pydantic-v1" }, { group = "pydantic-v2" }, ], [ { group = "pydantic-v1" }, { extra = "mcp" }, ], ] [dependency-groups] # version pins are in uv.lock dev = [ "pyright==1.1.399", "mypy==1.17", "respx", "pytest", "pytest-asyncio", "ruff", "time-machine", "dirty-equals>=0.6.0", "importlib-metadata>=6.7.0", "boto3-stubs >= 1", "rich>=13.7.1", "pytest-xdist>=3.6.1", "inline-snapshot>=0.28.0", "griffe>=1", "http-snapshot[httpx]==0.1.9", ] pydantic-v1 = [ "pydantic>=1.9.0,<2", ] pydantic-v2 = [ "pydantic~=2.0 ; python_full_version < '3.14'", "pydantic~=2.12 ; python_full_version >= '3.14'", ] [build-system] requires = ["hatchling==1.26.3", "hatch-fancy-pypi-readme"] build-backend = "hatchling.build" [tool.hatch.build] include = [ "src/*" ] [tool.hatch.build.targets.wheel] packages = ["src/anthropic"] [tool.hatch.build.targets.sdist] # Basically everything except hidden files/directories (such as .github, .devcontainers, .python-version, etc) include = [ "/*.toml", "/*.json", "/*.lock", "/*.md", "/mypy.ini", "/noxfile.py", "bin/*", "examples/*", "src/*", "tests/*", ] [tool.hatch.metadata.hooks.fancy-pypi-readme] content-type = "text/markdown" [[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]] path = "README.md" [[tool.hatch.metadata.hooks.fancy-pypi-readme.substitutions]] # replace relative links with absolute links pattern = '\[(.+?)\]\(((?!https?://)\S+?)\)' replacement = '[\1](https://github.com/anthropics/anthropic-sdk-python/tree/main/\g<2>)' [tool.pytest.ini_options] testpaths = ["tests"] addopts = "--tb=short -n auto" xfail_strict = true asyncio_mode = "auto" asyncio_default_fixture_loop_scope = "session" filterwarnings = [ "error" ] [tool.inline-snapshot] format-command="ruff format --stdin-filename {filename}" [tool.pyright] # this enables practically every flag given by pyright. # there are a couple of flags that are still disabled by # default in strict mode as they are experimental and niche. typeCheckingMode = "strict" pythonVersion = "3.9" exclude = [ ".git", "_dev", ".venv", ".nox", "examples/mcp_tool_runner.py", # mcp requires Python 3.10+, lint runs on 3.9 ] reportImplicitOverride = true reportOverlappingOverload = false reportImportCycles = false reportPrivateUsage = false [tool.mypy] pretty = true show_error_codes = true # Exclude _files.py because mypy isn't smart enough to apply # the correct type narrowing and as this is an internal module # it's fine to just use Pyright. # # We also exclude our `tests` as mypy doesn't always infer # types correctly and Pyright will still catch any type errors. exclude = ["src/anthropic/_files.py", "_dev/.*.py", "tests/.*", "examples/mcp_server_weather.py", "examples/mcp_tool_runner.py", "examples/tools_with_mcp.py", "examples/memory/basic.py", "src/anthropic/lib/_parse/_transform.py", "src/anthropic/lib/tools/_beta_functions.py"] strict_equality = true implicit_reexport = true check_untyped_defs = true no_implicit_optional = true warn_return_any = true warn_unreachable = true warn_unused_configs = true # Turn these options off as it could cause conflicts # with the Pyright options. warn_unused_ignores = false warn_redundant_casts = false disallow_any_generics = true disallow_untyped_defs = true disallow_untyped_calls = true disallow_subclassing_any = true disallow_incomplete_defs = true disallow_untyped_decorators = true cache_fine_grained = true # By default, mypy reports an error if you assign a value to the result # of a function call that doesn't return anything. We do this in our test # cases: # ``` # result = ... # assert result is None # ``` # Changing this codegen to make mypy happy would increase complexity # and would not be worth it. disable_error_code = "func-returns-value,overload-cannot-match" # https://github.com/python/mypy/issues/12162 [[tool.mypy.overrides]] module = "black.files.*" ignore_errors = true ignore_missing_imports = true [[tool.mypy.overrides]] module = "anthropic.lib.vertex._auth" disallow_untyped_calls = false [[tool.mypy.overrides]] module = "tests.lib.tools.test_mcp_tool" follow_imports = "skip" [tool.ruff] line-length = 120 output-format = "grouped" target-version = "py38" [tool.ruff.format] docstring-code-format = true [tool.ruff.lint] select = [ # isort "I", # bugbear rules "B", # remove unused imports "F401", # check for missing future annotations "FA102", # bare except statements "E722", # unused arguments "ARG", # print statements "T201", "T203", # misuse of typing.TYPE_CHECKING "TC004", # import rules "TID251", ] ignore = [ # mutable defaults "B006", ] unfixable = [ # disable auto fix for print statements "T201", "T203", ] extend-safe-fixes = ["FA102"] [tool.ruff.lint.flake8-tidy-imports.banned-api] "functools.lru_cache".msg = "This function does not retain type information for the wrapped function's arguments; The `lru_cache` function from `_utils` should be used instead" [tool.ruff.lint.isort] length-sort = true length-sort-straight = true combine-as-imports = true extra-standard-library = ["typing_extensions"] known-first-party = ["anthropic", "tests"] [tool.ruff.lint.per-file-ignores] "bin/**.py" = ["T201", "T203"] "scripts/**.py" = ["T201", "T203"] "tests/**.py" = ["T201", "T203"] "examples/**.py" = ["T201", "T203"] anthropic-sdk-python-0.120.2/release-please-config.json000066400000000000000000000024551523216435200227770ustar00rootroot00000000000000{ "packages": { ".": {} }, "$schema": "https://raw.githubusercontent.com/stainless-api/release-please/main/schemas/config.json", "include-v-in-tag": true, "include-component-in-tag": false, "versioning": "prerelease", "prerelease": true, "bump-minor-pre-major": true, "bump-patch-for-minor-pre-major": false, "pull-request-header": "Automated Release PR", "pull-request-title-pattern": "release: ${version}", "changelog-sections": [ { "type": "feat", "section": "Features" }, { "type": "fix", "section": "Bug Fixes" }, { "type": "perf", "section": "Performance Improvements" }, { "type": "revert", "section": "Reverts" }, { "type": "chore", "section": "Chores" }, { "type": "docs", "section": "Documentation" }, { "type": "style", "section": "Styles" }, { "type": "refactor", "section": "Refactors" }, { "type": "test", "section": "Tests", "hidden": true }, { "type": "build", "section": "Build System" }, { "type": "ci", "section": "Continuous Integration", "hidden": true } ], "release-type": "python", "extra-files": [ "src/anthropic/_version.py" ] }anthropic-sdk-python-0.120.2/requirements-dev.lock000066400000000000000000000063361523216435200221250ustar00rootroot00000000000000# This file was autogenerated by uv via the following command: # uv export -o requirements-dev.lock --no-hashes -e . annotated-types==0.7.0 # via pydantic anyio==4.12.1 # via # anthropic # httpx asttokens==3.0.1 # via inline-snapshot backports-asyncio-runner==1.2.0 ; python_full_version < '3.11' # via pytest-asyncio boto3-stubs==1.42.69 botocore-stubs==1.42.41 # via boto3-stubs certifi==2026.2.25 # via # httpcore # httpx colorama==0.4.6 # via # griffe # griffecli # pytest dirty-equals==0.11 distro==1.9.0 # via anthropic docstring-parser==0.17.0 # via anthropic exceptiongroup==1.3.1 ; python_full_version < '3.11' # via # anyio # pytest execnet==2.1.2 # via pytest-xdist executing==2.2.1 # via inline-snapshot griffe==1.14.0 ; python_full_version < '3.10' griffe==2.0.0 ; python_full_version >= '3.10' griffecli==2.0.0 ; python_full_version >= '3.10' # via griffe griffelib==2.0.0 ; python_full_version >= '3.10' # via # griffe # griffecli h11==0.16.0 # via httpcore http-snapshot==0.1.9 httpcore==1.0.9 # via httpx httpx==0.28.1 # via # anthropic # http-snapshot # respx idna==3.11 # via # anyio # httpx importlib-metadata==8.7.1 iniconfig==2.1.0 ; python_full_version < '3.10' # via pytest iniconfig==2.3.0 ; python_full_version >= '3.10' # via pytest inline-snapshot==0.32.5 # via http-snapshot jiter==0.13.0 # via anthropic markdown-it-py==3.0.0 ; python_full_version < '3.10' # via rich markdown-it-py==4.0.0 ; python_full_version >= '3.10' # via rich mdurl==0.1.2 # via markdown-it-py mypy==1.17.0 mypy-extensions==1.1.0 # via mypy nodeenv==1.10.0 # via pyright packaging==26.0 # via pytest pathspec==1.0.4 # via mypy pluggy==1.6.0 # via pytest pydantic==2.12.5 # via anthropic pydantic-core==2.41.5 # via pydantic pygments==2.19.2 # via # pytest # rich pyright==1.1.399 pytest==8.4.2 ; python_full_version < '3.10' # via # inline-snapshot # pytest-asyncio # pytest-xdist pytest==9.0.2 ; python_full_version >= '3.10' # via # inline-snapshot # pytest-asyncio # pytest-xdist pytest-asyncio==1.2.0 ; python_full_version < '3.10' pytest-asyncio==1.3.0 ; python_full_version >= '3.10' pytest-xdist==3.8.0 python-dateutil==2.9.0.post0 ; python_full_version < '3.10' # via time-machine respx==0.22.0 rich==14.3.3 # via inline-snapshot ruff==0.15.6 six==1.17.0 ; python_full_version < '3.10' # via python-dateutil sniffio==1.3.1 # via anthropic time-machine==2.19.0 ; python_full_version < '3.10' time-machine==3.2.0 ; python_full_version >= '3.10' tomli==2.4.0 ; python_full_version < '3.11' # via # inline-snapshot # mypy # pytest types-awscrt==0.31.3 # via botocore-stubs types-s3transfer==0.16.0 # via boto3-stubs typing-extensions==4.15.0 # via # anthropic # anyio # boto3-stubs # exceptiongroup # inline-snapshot # mypy # pydantic # pydantic-core # pyright # pytest-asyncio # typing-inspection typing-inspection==0.4.2 # via pydantic zipp==3.23.0 # via importlib-metadata anthropic-sdk-python-0.120.2/scripts/000077500000000000000000000000001523216435200174335ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/scripts/bootstrap000077500000000000000000000012671523216435200214040ustar00rootroot00000000000000#!/usr/bin/env bash set -e cd "$(dirname "$0")/.." if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "${SKIP_BREW:-}" != "1" ] && [ -t 0 ]; then brew bundle check >/dev/null 2>&1 || { echo -n "==> Install Homebrew dependencies? (y/N): " read -r response case "$response" in [yY][eE][sS]|[yY]) brew bundle ;; *) ;; esac echo } fi echo "==> Installing Python…" uv python install echo "==> Installing Python dependencies…" uv sync --all-extras echo "==> Exporting Python dependencies…" # note: `--no-hashes` is required because of https://github.com/pypa/pip/issues/4995 uv export -o requirements-dev.lock --no-hashes anthropic-sdk-python-0.120.2/scripts/detect-breaking-changes000077500000000000000000000010251523216435200240150ustar00rootroot00000000000000#!/usr/bin/env bash set -e cd "$(dirname "$0")/.." echo "==> Detecting breaking changes" TEST_PATHS=( tests/api_resources tests/test_client.py tests/test_response.py tests/test_legacy_response.py ) for PATHSPEC in "${TEST_PATHS[@]}"; do # Try to check out previous versions of the test files # with the current SDK. git checkout "$1" -- "${PATHSPEC}" 2>/dev/null || true done # Instead of running the tests, use the linter to check if an # older test is no longer compatible with the latest SDK. ./scripts/lint anthropic-sdk-python-0.120.2/scripts/detect-breaking-changes.py000066400000000000000000000046721523216435200244540ustar00rootroot00000000000000from __future__ import annotations import sys from typing import Iterator from pathlib import Path import rich import griffe from rich.text import Text from rich.style import Style def public_members(obj: griffe.Object | griffe.Alias) -> dict[str, griffe.Object | griffe.Alias]: if isinstance(obj, griffe.Alias): # ignore imports for now, they're technically part of the public API # but we don't have good preventative measures in place to prevent # changing them return {} return {name: value for name, value in obj.all_members.items() if not name.startswith("_")} def find_breaking_changes( new_obj: griffe.Object | griffe.Alias, old_obj: griffe.Object | griffe.Alias, *, path: list[str], ) -> Iterator[Text | str]: new_members = public_members(new_obj) old_members = public_members(old_obj) for name, old_member in old_members.items(): if isinstance(old_member, griffe.Alias) and len(path) > 2: # ignore imports in `/types/` for now, they're technically part of the public API # but we don't have good preventative measures in place to prevent changing them continue new_member = new_members.get(name) if new_member is None: cls_name = old_member.__class__.__name__ yield Text(f"({cls_name})", style=Style(color="rgb(119, 119, 119)")) yield from [" " for _ in range(10 - len(cls_name))] yield f" {'.'.join(path)}.{name}" yield "\n" continue yield from find_breaking_changes(new_member, old_member, path=[*path, name]) def main() -> None: try: against_ref = sys.argv[1] except IndexError as err: raise RuntimeError("You must specify a base ref to run breaking change detection against") from err package = griffe.load( "anthropic", search_paths=[Path(__file__).parent.parent.joinpath("src")], ) old_package = griffe.load_git( "anthropic", ref=against_ref, search_paths=["src"], ) assert isinstance(package, griffe.Module) assert isinstance(old_package, griffe.Module) output = list(find_breaking_changes(package, old_package, path=["anthropic"])) if output: rich.print(Text("Breaking changes detected!", style=Style(color="rgb(165, 79, 87)"))) rich.print() for text in output: rich.print(text, end="") sys.exit(1) main() anthropic-sdk-python-0.120.2/scripts/format000077500000000000000000000005121523216435200206470ustar00rootroot00000000000000#!/usr/bin/env bash set -e cd "$(dirname "$0")/.." echo "==> Running ruff" uv run ruff format uv run ruff check --fix . # run formatting again to fix any inconsistencies when imports are stripped uv run ruff format echo "==> Formatting docs" uv run python scripts/utils/ruffen-docs.py README.md $(find . -type f -name api.md) anthropic-sdk-python-0.120.2/scripts/lint000077500000000000000000000005441523216435200203320ustar00rootroot00000000000000#!/usr/bin/env bash set -e cd "$(dirname "$0")/.." if [ "$1" = "--fix" ]; then echo "==> Running ruff with --fix" uv run ruff check . --fix else echo "==> Running ruff" uv run ruff check . fi echo "==> Running pyright" uv run pyright echo "==> Running mypy" uv run mypy . echo "==> Making sure it imports" uv run python -c 'import anthropic' anthropic-sdk-python-0.120.2/scripts/mock000077500000000000000000000030721523216435200203140ustar00rootroot00000000000000#!/usr/bin/env bash set -e cd "$(dirname "$0")/.." if [[ -n "$1" && "$1" != '--'* ]]; then URL="$1" shift else URL="$(grep 'openapi_spec_url' .stats.yml | cut -d' ' -f2)" fi # Check if the URL is empty if [ -z "$URL" ]; then echo "Error: No OpenAPI spec path/url provided or found in .stats.yml" exit 1 fi echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout npm exec --package=@stdy/cli@0.22.2 -- steady --version npm exec --package=@stdy/cli@0.22.2 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" attempts=0 while ! curl --silent --fail "http://127.0.0.1:4010/_x-steady/health" >/dev/null 2>&1; do if ! kill -0 $! 2>/dev/null; then echo cat .stdy.log exit 1 fi attempts=$((attempts + 1)) if [ "$attempts" -ge 300 ]; then echo echo "Timed out waiting for Steady server to start" cat .stdy.log exit 1 fi echo -n "." sleep 0.1 done echo else npm exec --package=@stdy/cli@0.22.2 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" fi anthropic-sdk-python-0.120.2/scripts/test000077500000000000000000000054521523216435200203460ustar00rootroot00000000000000#!/usr/bin/env bash set -e cd "$(dirname "$0")/.." RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[0;33m' NC='\033[0m' # No Color function steady_is_running() { curl --silent "http://127.0.0.1:4010/_x-steady/health" >/dev/null 2>&1 } kill_server_on_port() { pids=$(lsof -t -i tcp:"$1" || echo "") if [ "$pids" != "" ]; then kill "$pids" echo "Stopped $pids." fi } function is_overriding_api_base_url() { [ -n "$TEST_API_BASE_URL" ] } if ! is_overriding_api_base_url && ! steady_is_running ; then # When we exit this script, make sure to kill the background mock server process trap 'kill_server_on_port 4010' EXIT # Start the dev server ./scripts/mock --daemon fi if is_overriding_api_base_url ; then echo -e "${GREEN}✔ Running tests against ${TEST_API_BASE_URL}${NC}" echo elif ! steady_is_running ; then echo -e "${RED}ERROR:${NC} The test suite will not run without a mock Steady server" echo -e "running against your OpenAPI spec." echo echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.22.2 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" echo exit 1 else echo -e "${GREEN}✔ Mock steady server is running with your OpenAPI spec${NC}" echo fi export DEFER_PYDANTIC_BUILD=false # Note that we need to specify the patch version here so that uv # won't use unstable (alpha, beta, rc) releases for the tests PY_VERSION_MIN=">=3.9.0" PY_VERSION_MAX=">=3.14.0" function run_tests() { echo "==> Running tests with Pydantic v2" uv run --isolated --all-extras pytest "$@" # Skip Pydantic v1 tests on latest Python (not supported) if [[ "$UV_PYTHON" != "$PY_VERSION_MAX" ]]; then echo "==> Running tests with Pydantic v1" uv run --isolated --all-extras --no-extra=mcp --group=pydantic-v1 pytest "$@" fi # The lockfile pins one mcp major; also run the mcp tests against the other one # (mcp requires Python 3.10+, so skip on the minimum version) if [[ "$UV_PYTHON" != "$PY_VERSION_MIN" ]]; then echo "==> Running MCP tests with mcp v2" uv run --isolated --all-extras --with 'mcp>=2' pytest tests/lib/tools/test_mcp_tool.py fi } # If UV_PYTHON is already set in the environment, just run the command once if [[ -n "$UV_PYTHON" ]]; then run_tests "$@" else # If UV_PYTHON is not set, run the command for min and max versions echo "==> Running tests for Python $PY_VERSION_MIN" UV_PYTHON="$PY_VERSION_MIN" run_tests "$@" echo "==> Running tests for Python $PY_VERSION_MAX" UV_PYTHON="$PY_VERSION_MAX" run_tests "$@" fi anthropic-sdk-python-0.120.2/scripts/utils/000077500000000000000000000000001523216435200205735ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/scripts/utils/ruffen-docs.py000066400000000000000000000121401523216435200233560ustar00rootroot00000000000000# fork of https://github.com/asottile/blacken-docs adapted for ruff from __future__ import annotations import re import sys import argparse import textwrap import contextlib import subprocess from typing import Match, Optional, Sequence, Generator, NamedTuple, cast MD_RE = re.compile( r"(?P^(?P *)```\s*python\n)" r"(?P.*?)" r"(?P^(?P=indent)```\s*$)", re.DOTALL | re.MULTILINE, ) MD_PYCON_RE = re.compile( r"(?P^(?P *)```\s*pycon\n)" r"(?P.*?)" r"(?P^(?P=indent)```.*$)", re.DOTALL | re.MULTILINE, ) PYCON_PREFIX = ">>> " PYCON_CONTINUATION_PREFIX = "..." PYCON_CONTINUATION_RE = re.compile( rf"^{re.escape(PYCON_CONTINUATION_PREFIX)}( |$)", ) DEFAULT_LINE_LENGTH = 100 class CodeBlockError(NamedTuple): offset: int exc: Exception def format_str( src: str, ) -> tuple[str, Sequence[CodeBlockError]]: errors: list[CodeBlockError] = [] @contextlib.contextmanager def _collect_error(match: Match[str]) -> Generator[None, None, None]: try: yield except Exception as e: errors.append(CodeBlockError(match.start(), e)) def _md_match(match: Match[str]) -> str: code = textwrap.dedent(match["code"]) with _collect_error(match): code = format_code_block(code) code = textwrap.indent(code, match["indent"]) return f"{match['before']}{code}{match['after']}" def _pycon_match(match: Match[str]) -> str: code = "" fragment = cast(Optional[str], None) def finish_fragment() -> None: nonlocal code nonlocal fragment if fragment is not None: with _collect_error(match): fragment = format_code_block(fragment) fragment_lines = fragment.splitlines() code += f"{PYCON_PREFIX}{fragment_lines[0]}\n" for line in fragment_lines[1:]: # Skip blank lines to handle Black adding a blank above # functions within blocks. A blank line would end the REPL # continuation prompt. # # >>> if True: # ... def f(): # ... pass # ... if line: code += f"{PYCON_CONTINUATION_PREFIX} {line}\n" if fragment_lines[-1].startswith(" "): code += f"{PYCON_CONTINUATION_PREFIX}\n" fragment = None indentation = None for line in match["code"].splitlines(): orig_line, line = line, line.lstrip() if indentation is None and line: indentation = len(orig_line) - len(line) continuation_match = PYCON_CONTINUATION_RE.match(line) if continuation_match and fragment is not None: fragment += line[continuation_match.end() :] + "\n" else: finish_fragment() if line.startswith(PYCON_PREFIX): fragment = line[len(PYCON_PREFIX) :] + "\n" else: code += orig_line[indentation:] + "\n" finish_fragment() return code def _md_pycon_match(match: Match[str]) -> str: code = _pycon_match(match) code = textwrap.indent(code, match["indent"]) return f"{match['before']}{code}{match['after']}" src = MD_RE.sub(_md_match, src) src = MD_PYCON_RE.sub(_md_pycon_match, src) return src, errors def format_code_block(code: str) -> str: return subprocess.check_output( [ sys.executable, "-m", "ruff", "format", "--stdin-filename=script.py", f"--line-length={DEFAULT_LINE_LENGTH}", ], encoding="utf-8", input=code, ) def format_file( filename: str, skip_errors: bool, ) -> int: with open(filename, encoding="UTF-8") as f: contents = f.read() new_contents, errors = format_str(contents) for error in errors: lineno = contents[: error.offset].count("\n") + 1 print(f"{filename}:{lineno}: code block parse error {error.exc}") if errors and not skip_errors: return 1 if contents != new_contents: print(f"{filename}: Rewriting...") with open(filename, "w", encoding="UTF-8") as f: f.write(new_contents) return 0 else: return 0 def main(argv: Sequence[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument( "-l", "--line-length", type=int, default=DEFAULT_LINE_LENGTH, ) parser.add_argument( "-S", "--skip-string-normalization", action="store_true", ) parser.add_argument("-E", "--skip-errors", action="store_true") parser.add_argument("filenames", nargs="*") args = parser.parse_args(argv) retv = 0 for filename in args.filenames: retv |= format_file(filename, skip_errors=args.skip_errors) return retv if __name__ == "__main__": raise SystemExit(main()) anthropic-sdk-python-0.120.2/scripts/utils/upload-artifact.sh000077500000000000000000000014371523216435200242160ustar00rootroot00000000000000#!/usr/bin/env bash set -exuo pipefail FILENAME=$(basename dist/*.whl) RESPONSE=$(curl -X POST "$URL?filename=$FILENAME" \ -H "Authorization: Bearer $AUTH" \ -H "Content-Type: application/json") SIGNED_URL=$(echo "$RESPONSE" | jq -r '.url') if [[ "$SIGNED_URL" == "null" ]]; then echo -e "\033[31mFailed to get signed URL.\033[0m" exit 1 fi UPLOAD_RESPONSE=$(curl -v -X PUT \ -H "Content-Type: binary/octet-stream" \ --data-binary "@dist/$FILENAME" "$SIGNED_URL" 2>&1) if echo "$UPLOAD_RESPONSE" | grep -q "HTTP/[0-9.]* 200"; then echo -e "\033[32mUploaded build to Stainless storage.\033[0m" echo -e "\033[32mInstallation: pip install 'https://pkg.stainless.com/s/anthropic-python/$SHA/$FILENAME'\033[0m" else echo -e "\033[31mFailed to upload artifact.\033[0m" exit 1 fi anthropic-sdk-python-0.120.2/src/000077500000000000000000000000001523216435200165335ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/000077500000000000000000000000001523216435200205225ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/__init__.py000066400000000000000000000100211523216435200226250ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. import typing as _t from . import types from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes, omit, not_given from ._utils import file_from_path from ._client import ( Client, Stream, Timeout, Anthropic, Transport, AsyncClient, AsyncStream, AsyncAnthropic, RequestOptions, ) from ._models import BaseModel from ._request import APIRequest from ._version import __title__, __version__ from ._response import APIResponse as APIResponse, AsyncAPIResponse as AsyncAPIResponse from ._constants import ( AI_PROMPT as AI_PROMPT, HUMAN_PROMPT as HUMAN_PROMPT, DEFAULT_TIMEOUT, DEFAULT_MAX_RETRIES, DEFAULT_CONNECTION_LIMITS, ) from ._exceptions import ( APIError, ConflictError, NotFoundError, AnthropicError, APIStatusError, RateLimitError, RetryableError, APITimeoutError, BadRequestError, OverloadedError, APIConnectionError, AuthenticationError, InternalServerError, RequestTooLargeError, PermissionDeniedError, UnprocessableEntityError, APIWebhookValidationError, APIResponseValidationError, ) from ._middleware import ( CallNext, Middleware, AsyncCallNext, MiddlewareInput, MiddlewareCallable, AsyncMiddlewareCallable, ) from ._base_client import DefaultHttpxClient, DefaultAioHttpClient, DefaultAsyncHttpxClient from ._utils._logs import setup_logging as _setup_logging from .lib.middleware import BetaFallbackState, BetaRefusalFallbackMiddleware from .lib._parse._transform import transform_schema __all__ = [ "types", "__version__", "__title__", "NoneType", "Transport", "ProxiesTypes", "NotGiven", "NOT_GIVEN", "not_given", "Omit", "omit", "AnthropicError", "APIError", "APIStatusError", "APITimeoutError", "APIConnectionError", "APIResponseValidationError", "APIWebhookValidationError", "BadRequestError", "AuthenticationError", "PermissionDeniedError", "NotFoundError", "ConflictError", "RequestTooLargeError", "UnprocessableEntityError", "RateLimitError", "InternalServerError", "OverloadedError", "RetryableError", "Timeout", "RequestOptions", "Client", "AsyncClient", "Stream", "AsyncStream", "Anthropic", "AsyncAnthropic", "APIRequest", "Middleware", "MiddlewareInput", "MiddlewareCallable", "AsyncMiddlewareCallable", "CallNext", "AsyncCallNext", "BetaFallbackState", "BetaRefusalFallbackMiddleware", "file_from_path", "BaseModel", "DEFAULT_TIMEOUT", "DEFAULT_MAX_RETRIES", "DEFAULT_CONNECTION_LIMITS", "DefaultHttpxClient", "DefaultAsyncHttpxClient", "DefaultAioHttpClient", "HUMAN_PROMPT", "AI_PROMPT", "beta_tool", "beta_async_tool", "transform_schema", ] if not _t.TYPE_CHECKING: from ._utils._resources_proxy import resources as resources from .lib.aws import AnthropicAWS as AnthropicAWS, AsyncAnthropicAWS as AsyncAnthropicAWS from .lib.tools import beta_tool, beta_async_tool from .lib.vertex import * from .lib.bedrock import * from .lib.foundry import AnthropicFoundry as AnthropicFoundry, AsyncAnthropicFoundry as AsyncAnthropicFoundry from .lib.streaming import * from .lib.credentials import * from .lib.google_cloud import ( AnthropicGoogleCloud as AnthropicGoogleCloud, AsyncAnthropicGoogleCloud as AsyncAnthropicGoogleCloud, ) _setup_logging() # Update the __module__ attribute for exported symbols so that # error messages point to this module instead of the module # it was originally defined in, e.g. # anthropic._exceptions.NotFoundError -> anthropic.NotFoundError __locals = locals() for __name in __all__: if not __name.startswith("__"): try: __locals[__name].__module__ = "anthropic" except (TypeError, AttributeError): # Some of our exported symbols are builtins which we can't set attributes for. pass anthropic-sdk-python-0.120.2/src/anthropic/_base_client.py000066400000000000000000002747671523216435200235320ustar00rootroot00000000000000from __future__ import annotations import sys import json import time import uuid import email import socket import asyncio import inspect import logging import platform import warnings import email.utils from types import TracebackType from random import random from typing import ( TYPE_CHECKING, Any, Dict, List, Type, Tuple, Union, Generic, Mapping, TypeVar, Iterable, Iterator, Optional, Sequence, Generator, AsyncIterator, cast, overload, ) from typing_extensions import Literal, override, get_origin import anyio import httpx import distro import pydantic from httpx import URL, Proxy, HTTPTransport, AsyncHTTPTransport from pydantic import PrivateAttr from . import _exceptions from ._qs import Querystring from ._files import to_httpx_files, async_to_httpx_files from ._types import ( Body, Omit, Query, Headers, Timeout, NotGiven, ResponseT, AnyMapping, PostParser, BinaryTypes, RequestFiles, HttpxSendArgs, RequestOptions, AsyncBinaryTypes, HttpxRequestFiles, ModelBuilderProtocol, not_given, ) from ._utils import is_dict, is_list, asyncify, is_given, lru_cache, is_mapping from ._compat import PYDANTIC_V1, model_copy, model_dump from ._models import GenericModel, FinalRequestOptions, validate_type, construct_type from ._request import APIRequest from ._response import ( APIResponse, BaseAPIResponse, AsyncAPIResponse, extract_response_type, ) from ._constants import ( DEFAULT_TIMEOUT, MAX_RETRY_DELAY, DEFAULT_MAX_RETRIES, INITIAL_RETRY_DELAY, RAW_RESPONSE_HEADER, OVERRIDE_CAST_TO_HEADER, DEFAULT_CONNECTION_LIMITS, ) from ._streaming import Stream, SSEDecoder, AsyncStream, SSEBytesDecoder from ._exceptions import ( AnthropicError, APIStatusError, RetryableError, APITimeoutError, APIConnectionError, APIResponseValidationError, ) from ._middleware import ( CallNext, Middleware, AsyncCallNext, MiddlewareInput, MiddlewareCallable, AsyncMiddlewareCallable, validate_sync_middleware, validate_async_middleware, ) from ._utils._json import openapi_dumps from ._utils._httpx import get_environment_proxies from ._legacy_response import LegacyAPIResponse log: logging.Logger = logging.getLogger(__name__) # TODO: make base page type vars covariant SyncPageT = TypeVar("SyncPageT", bound="BaseSyncPage[Any]") AsyncPageT = TypeVar("AsyncPageT", bound="BaseAsyncPage[Any]") _T = TypeVar("_T") _T_co = TypeVar("_T_co", covariant=True) _StreamT = TypeVar("_StreamT", bound=Stream[Any]) _AsyncStreamT = TypeVar("_AsyncStreamT", bound=AsyncStream[Any]) if TYPE_CHECKING: from httpx._config import ( DEFAULT_TIMEOUT_CONFIG, # pyright: ignore[reportPrivateImportUsage] ) HTTPX_DEFAULT_TIMEOUT = DEFAULT_TIMEOUT_CONFIG else: try: from httpx._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT except ImportError: # taken from https://github.com/encode/httpx/blob/3ba5fe0d7ac70222590e759c31442b1cab263791/httpx/_config.py#L366 HTTPX_DEFAULT_TIMEOUT = Timeout(5.0) class PageInfo: """Stores the necessary information to build the request to retrieve the next page. Either `url` or `params` must be set. """ url: URL | NotGiven params: Query | NotGiven json: Body | NotGiven @overload def __init__( self, *, url: URL, ) -> None: ... @overload def __init__( self, *, params: Query, ) -> None: ... @overload def __init__( self, *, json: Body, ) -> None: ... def __init__( self, *, url: URL | NotGiven = not_given, json: Body | NotGiven = not_given, params: Query | NotGiven = not_given, ) -> None: self.url = url self.json = json self.params = params @override def __repr__(self) -> str: if self.url: return f"{self.__class__.__name__}(url={self.url})" if self.json: return f"{self.__class__.__name__}(json={self.json})" return f"{self.__class__.__name__}(params={self.params})" class BasePage(GenericModel, Generic[_T]): """ Defines the core interface for pagination. Type Args: ModelT: The pydantic model that represents an item in the response. Methods: has_next_page(): Check if there is another page available next_page_info(): Get the necessary information to make a request for the next page """ _options: FinalRequestOptions = PrivateAttr() _model: Type[_T] = PrivateAttr() def has_next_page(self) -> bool: items = self._get_page_items() if not items: return False return self.next_page_info() is not None def next_page_info(self) -> Optional[PageInfo]: ... def _get_page_items(self) -> Iterable[_T]: # type: ignore[empty-body] ... def _params_from_url(self, url: URL) -> httpx.QueryParams: # TODO: do we have to preprocess params here? return httpx.QueryParams(cast(Any, self._options.params)).merge(url.params) def _info_to_options(self, info: PageInfo) -> FinalRequestOptions: options = model_copy(self._options) options._strip_raw_response_header() if not isinstance(info.params, NotGiven): options.params = {**options.params, **info.params} return options if not isinstance(info.url, NotGiven): params = self._params_from_url(info.url) url = info.url.copy_with(params=params) options.params = dict(url.params) options.url = str(url) return options if not isinstance(info.json, NotGiven): if not is_mapping(info.json): raise TypeError("Pagination is only supported with mappings") if not options.json_data: options.json_data = {**info.json} else: if not is_mapping(options.json_data): raise TypeError("Pagination is only supported with mappings") options.json_data = {**options.json_data, **info.json} return options raise ValueError("Unexpected PageInfo state") class BaseSyncPage(BasePage[_T], Generic[_T]): _client: SyncAPIClient = pydantic.PrivateAttr() def _set_private_attributes( self, client: SyncAPIClient, model: Type[_T], options: FinalRequestOptions, ) -> None: if (not PYDANTIC_V1) and getattr(self, "__pydantic_private__", None) is None: self.__pydantic_private__ = {} self._model = model self._client = client self._options = options # Pydantic uses a custom `__iter__` method to support casting BaseModels # to dictionaries. e.g. dict(model). # As we want to support `for item in page`, this is inherently incompatible # with the default pydantic behaviour. It is not possible to support both # use cases at once. Fortunately, this is not a big deal as all other pydantic # methods should continue to work as expected as there is an alternative method # to cast a model to a dictionary, model.dict(), which is used internally # by pydantic. def __iter__(self) -> Iterator[_T]: # type: ignore for page in self.iter_pages(): for item in page._get_page_items(): yield item def iter_pages(self: SyncPageT) -> Iterator[SyncPageT]: page = self while True: yield page if page.has_next_page(): page = page.get_next_page() else: return def get_next_page(self: SyncPageT) -> SyncPageT: info = self.next_page_info() if not info: raise RuntimeError( "No next page expected; please check `.has_next_page()` before calling `.get_next_page()`." ) options = self._info_to_options(info) return self._client._request_api_list(self._model, page=self.__class__, options=options) class AsyncPaginator(Generic[_T, AsyncPageT]): def __init__( self, client: AsyncAPIClient, options: FinalRequestOptions, page_cls: Type[AsyncPageT], model: Type[_T], ) -> None: self._model = model self._client = client self._options = options self._page_cls = page_cls def __await__(self) -> Generator[Any, None, AsyncPageT]: return self._get_page().__await__() async def _get_page(self) -> AsyncPageT: def _parser(resp: AsyncPageT) -> AsyncPageT: resp._set_private_attributes( model=self._model, options=self._options, client=self._client, ) return resp self._options.post_parser = _parser return await self._client.request(self._page_cls, self._options) async def __aiter__(self) -> AsyncIterator[_T]: # https://github.com/microsoft/pyright/issues/3464 page = cast( AsyncPageT, await self, # type: ignore ) async for item in page: yield item class BaseAsyncPage(BasePage[_T], Generic[_T]): _client: AsyncAPIClient = pydantic.PrivateAttr() def _set_private_attributes( self, model: Type[_T], client: AsyncAPIClient, options: FinalRequestOptions, ) -> None: if (not PYDANTIC_V1) and getattr(self, "__pydantic_private__", None) is None: self.__pydantic_private__ = {} self._model = model self._client = client self._options = options async def __aiter__(self) -> AsyncIterator[_T]: async for page in self.iter_pages(): for item in page._get_page_items(): yield item async def iter_pages(self: AsyncPageT) -> AsyncIterator[AsyncPageT]: page = self while True: yield page if page.has_next_page(): page = await page.get_next_page() else: return async def get_next_page(self: AsyncPageT) -> AsyncPageT: info = self.next_page_info() if not info: raise RuntimeError( "No next page expected; please check `.has_next_page()` before calling `.get_next_page()`." ) options = self._info_to_options(info) return await self._client._request_api_list(self._model, page=self.__class__, options=options) _HttpxClientT = TypeVar("_HttpxClientT", bound=Union[httpx.Client, httpx.AsyncClient]) _DefaultStreamT = TypeVar("_DefaultStreamT", bound=Union[Stream[Any], AsyncStream[Any]]) class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]): _client: _HttpxClientT _version: str _base_url: URL max_retries: int timeout: Union[float, Timeout, None] _strict_response_validation: bool _idempotency_header: str | None _default_stream_cls: type[_DefaultStreamT] | None = None _middleware: tuple[MiddlewareInput, ...] def __init__( self, *, version: str, base_url: str | URL, _strict_response_validation: bool, max_retries: int = DEFAULT_MAX_RETRIES, timeout: float | Timeout | None = DEFAULT_TIMEOUT, custom_headers: Mapping[str, str] | None = None, custom_query: Mapping[str, object] | None = None, middleware: Sequence[MiddlewareInput] | None = None, ) -> None: self._version = version self._base_url = self._enforce_trailing_slash(URL(base_url)) self.max_retries = max_retries self.timeout = timeout self._custom_headers = custom_headers or {} self._custom_query = custom_query or {} self._strict_response_validation = _strict_response_validation self._idempotency_header = None self._platform: Platform | None = None self._middleware = tuple(middleware or ()) if max_retries is None: # pyright: ignore[reportUnnecessaryComparison] raise TypeError( "max_retries cannot be None. If you want to disable retries, pass `0`; if you want unlimited retries, pass `math.inf` or a very high number; if you want the default behavior, pass `anthropic.DEFAULT_MAX_RETRIES`" ) def _enforce_trailing_slash(self, url: URL) -> URL: if url.raw_path.endswith(b"/"): return url return url.copy_with(raw_path=url.raw_path + b"/") def _make_status_error_from_response( self, response: httpx.Response, ) -> APIStatusError: if response.is_closed and not response.is_stream_consumed: # We can't read the response body as it has been closed # before it was read. This can happen if an event hook # raises a status error. body = None err_msg = f"Error code: {response.status_code}" else: err_text = response.text.strip() body = err_text try: body = json.loads(err_text) err_msg = f"Error code: {response.status_code} - {body}" except Exception: err_msg = err_text or f"Error code: {response.status_code}" return self._make_status_error(err_msg, body=body, response=response) def _make_status_error( self, err_msg: str, *, body: object, response: httpx.Response, ) -> _exceptions.APIStatusError: raise NotImplementedError() def _build_headers(self, options: FinalRequestOptions, *, retries_taken: int = 0) -> httpx.Headers: custom_headers = options.headers or {} merged_headers = merge_headers( { "x-stainless-timeout": str(options.timeout.read) if isinstance(options.timeout, Timeout) else str(options.timeout), **self.default_headers, }, custom_headers, ) headers_dict = _strip_omit(merged_headers) self._validate_headers(headers_dict, custom_headers) # headers are case-insensitive while dictionaries are not. headers = httpx.Headers(headers_dict) idempotency_header = self._idempotency_header if idempotency_header and options.idempotency_key and idempotency_header not in headers: headers[idempotency_header] = options.idempotency_key # Don't set these headers if they were already set or removed by the caller. We check # `custom_headers`, which can contain `Omit()`, instead of `headers` to account for the removal case. lower_custom_headers = [header.lower() for header in custom_headers] if "x-stainless-retry-count" not in lower_custom_headers: headers["x-stainless-retry-count"] = str(retries_taken) if "x-stainless-read-timeout" not in lower_custom_headers: timeout = self.timeout if isinstance(options.timeout, NotGiven) else options.timeout if isinstance(timeout, Timeout): timeout = timeout.read if timeout is not None: headers["x-stainless-read-timeout"] = str(timeout) return headers def _prepare_url(self, url: str) -> URL: """ Merge a URL argument together with any 'base_url' on the client, to create the URL used for the outgoing request. """ # Copied from httpx's `_merge_url` method. merge_url = URL(url) if merge_url.is_relative_url: merge_raw_path = self.base_url.raw_path + merge_url.raw_path.lstrip(b"/") return self.base_url.copy_with(raw_path=merge_raw_path) return merge_url def _make_sse_decoder(self) -> SSEDecoder | SSEBytesDecoder: return SSEDecoder() def _build_request( self, options: FinalRequestOptions, *, retries_taken: int = 0, ) -> httpx.Request: if log.isEnabledFor(logging.DEBUG): log.debug( "Request options: %s", model_dump( options, exclude_unset=True, # Pydantic v1 can't dump every type we support in content, so we exclude it for now. exclude={ "content", } if PYDANTIC_V1 else {}, ), ) kwargs: dict[str, Any] = {} json_data = options.json_data if options.extra_json is not None: if json_data is None: json_data = cast(Body, options.extra_json) elif is_mapping(json_data): json_data = _merge_mappings(json_data, options.extra_json) else: raise RuntimeError(f"Unexpected JSON data type, {type(json_data)}, cannot merge with `extra_body`") headers = self._build_headers(options, retries_taken=retries_taken) params = _merge_mappings(self.default_query, options.params) content_type = headers.get("Content-Type") files = options.files # If the given Content-Type header is multipart/form-data then it # has to be removed so that httpx can generate the header with # additional information for us as it has to be in this form # for the server to be able to correctly parse the request: # multipart/form-data; boundary=---abc-- if content_type is not None and content_type.startswith("multipart/form-data"): if "boundary" not in content_type: # only remove the header if the boundary hasn't been explicitly set # as the caller doesn't want httpx to come up with their own boundary headers.pop("Content-Type") # As we are now sending multipart/form-data instead of application/json # we need to tell httpx to use it, https://www.python-httpx.org/advanced/clients/#multipart-file-encoding if json_data: if not is_dict(json_data): raise TypeError( f"Expected query input to be a dictionary for multipart requests but got {type(json_data)} instead." ) kwargs["data"] = self._serialize_multipartform(json_data) # httpx determines whether or not to send a "multipart/form-data" # request based on the truthiness of the "files" argument. # This gets around that issue by generating a dict value that # evaluates to true. # # https://github.com/encode/httpx/discussions/2399#discussioncomment-3814186 if not files: files = cast(HttpxRequestFiles, ForceMultipartDict()) prepared_url = self._prepare_url(options.url) # preserve hard-coded query params from the url if params and prepared_url.query: params = {**dict(prepared_url.params.items()), **params} prepared_url = prepared_url.copy_with(raw_path=prepared_url.raw_path.split(b"?", 1)[0]) if "_" in prepared_url.host: # work around https://github.com/encode/httpx/discussions/2880 kwargs["extensions"] = {"sni_hostname": prepared_url.host.replace("_", "-")} is_body_allowed = options.method.lower() != "get" if is_body_allowed: if options.content is not None and json_data is not None: raise TypeError("Passing both `content` and `json_data` is not supported") if options.content is not None and files is not None: raise TypeError("Passing both `content` and `files` is not supported") if options.content is not None: kwargs["content"] = options.content elif isinstance(json_data, bytes): kwargs["content"] = json_data elif not files: # Don't set content when JSON is sent as multipart/form-data, # since httpx's content param overrides other body arguments kwargs["content"] = openapi_dumps(json_data) if is_given(json_data) and json_data is not None else None kwargs["files"] = files else: headers.pop("Content-Type", None) kwargs.pop("data", None) # TODO: report this error to httpx return self._client.build_request( # pyright: ignore[reportUnknownMemberType] headers=headers, timeout=self.timeout if isinstance(options.timeout, NotGiven) else options.timeout, method=options.method, url=prepared_url, # the `Query` type that we use is incompatible with qs' # `Params` type as it needs to be typed as `Mapping[str, object]` # so that passing a `TypedDict` doesn't cause an error. # https://github.com/microsoft/pyright/issues/3526#event-6715453066 params=self.qs.stringify(cast(Mapping[str, Any], params)) if params else None, **kwargs, ) def _serialize_multipartform(self, data: Mapping[object, object]) -> dict[str, object]: items = self.qs.stringify_items( # TODO: type ignore is required as stringify_items is well typed but we can't be # well typed without heavy validation. data, # type: ignore array_format="brackets", ) serialized: dict[str, object] = {} for key, value in items: existing = serialized.get(key) if not existing: serialized[key] = value continue # If a value has already been set for this key then that # means we're sending data like `array[]=[1, 2, 3]` and we # need to tell httpx that we want to send multiple values with # the same key which is done by using a list or a tuple. # # Note: 2d arrays should never result in the same key at both # levels so it's safe to assume that if the value is a list, # it was because we changed it to be a list. if is_list(existing): existing.append(value) else: serialized[key] = [existing, value] return serialized def _maybe_override_cast_to(self, cast_to: type[ResponseT], options: FinalRequestOptions) -> type[ResponseT]: if not is_given(options.headers): return cast_to # make a copy of the headers so we don't mutate user-input headers = dict(options.headers) # we internally support defining a temporary header to override the # default `cast_to` type for use with `.with_raw_response` and `.with_streaming_response` # see _response.py for implementation details override_cast_to = headers.pop(OVERRIDE_CAST_TO_HEADER, not_given) if is_given(override_cast_to): options.headers = headers return cast(Type[ResponseT], override_cast_to) return cast_to def _convert_to_legacy_response(self, source: BaseAPIResponse[Any]) -> LegacyAPIResponse[Any]: """Convert an `APIResponse` / `AsyncAPIResponse` into the equivalent `LegacyAPIResponse`.""" return LegacyAPIResponse( raw=source.http_response, cast_to=source._cast_to, client=self, stream=source._is_sse_stream, stream_cls=source._stream_cls, options=source._options, retries_taken=source.retries_taken, ) def _should_stream_response_body(self, request: httpx.Request) -> bool: return request.headers.get(RAW_RESPONSE_HEADER) == "stream" # type: ignore[no-any-return] def _process_response_data( self, *, data: object, cast_to: type[ResponseT], response: httpx.Response, ) -> ResponseT: if data is None: return cast(ResponseT, None) if cast_to is object: return cast(ResponseT, data) try: if inspect.isclass(cast_to) and issubclass(cast_to, ModelBuilderProtocol): return cast(ResponseT, cast_to.build(response=response, data=data)) if self._strict_response_validation: return cast(ResponseT, validate_type(type_=cast_to, value=data)) return cast(ResponseT, construct_type(type_=cast_to, value=data)) except pydantic.ValidationError as err: raise APIResponseValidationError(response=response, body=data) from err @property def qs(self) -> Querystring: return Querystring() @property def custom_auth(self) -> httpx.Auth | None: return None @property def auth_headers(self) -> dict[str, str]: return {} @property def default_headers(self) -> dict[str, str | Omit]: return { "Accept": "application/json", "Content-Type": "application/json", "User-Agent": self.user_agent, **self.platform_headers(), **self.auth_headers, **self._custom_headers, } @property def default_query(self) -> dict[str, object]: return { **self._custom_query, } def _validate_headers( self, headers: Headers, # noqa: ARG002 custom_headers: Headers, # noqa: ARG002 ) -> None: """Validate the given default headers and custom headers. Does nothing by default. """ return @property def user_agent(self) -> str: return f"{self.__class__.__name__}/Python {self._version}" @property def base_url(self) -> URL: return self._base_url @base_url.setter def base_url(self, url: URL | str) -> None: self._base_url = self._enforce_trailing_slash(url if isinstance(url, URL) else URL(url)) @property def middleware(self) -> tuple[MiddlewareInput, ...]: """The client-level middleware, outermost first. To run extra middleware for specific calls, derive a client with `client.with_options(middleware=[*client.middleware, extra])`. """ return self._middleware def platform_headers(self) -> Dict[str, str]: # the actual implementation is in a separate `lru_cache` decorated # function because adding `lru_cache` to methods will leak memory # https://github.com/python/cpython/issues/88476 return platform_headers(self._version, platform=self._platform) def _calculate_nonstreaming_timeout(self, max_tokens: int, max_nonstreaming_tokens: int | None) -> Timeout: maximum_time = 60 * 60 default_time = 60 * 10 expected_time = maximum_time * max_tokens / 128_000 if expected_time > default_time or (max_nonstreaming_tokens and max_tokens > max_nonstreaming_tokens): raise ValueError( "Streaming is required for operations that may take longer than 10 minutes. " + "See https://github.com/anthropics/anthropic-sdk-python#long-requests for more details", ) return Timeout( default_time, connect=5.0, ) def _parse_retry_after_header(self, response_headers: Optional[httpx.Headers] = None) -> float | None: """Returns a float of the number of seconds (not milliseconds) to wait after retrying, or None if unspecified. About the Retry-After header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After See also https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After#syntax """ if response_headers is None: return None # First, try the non-standard `retry-after-ms` header for milliseconds, # which is more precise than integer-seconds `retry-after` try: retry_ms_header = response_headers.get("retry-after-ms", None) return float(retry_ms_header) / 1000 except (TypeError, ValueError): pass # Next, try parsing `retry-after` header as seconds (allowing nonstandard floats). retry_header = response_headers.get("retry-after") try: # note: the spec indicates that this should only ever be an integer # but if someone sends a float there's no reason for us to not respect it return float(retry_header) except (TypeError, ValueError): pass # Last, try parsing `retry-after` as a date. retry_date_tuple = email.utils.parsedate_tz(retry_header) if retry_date_tuple is None: return None retry_date = email.utils.mktime_tz(retry_date_tuple) return float(retry_date - time.time()) def _calculate_retry_timeout( self, remaining_retries: int, options: FinalRequestOptions, response_headers: Optional[httpx.Headers] = None, ) -> float: max_retries = options.get_max_retries(self.max_retries) # If the API asks us to wait a certain amount of time (and it's a reasonable amount), just do what it says. retry_after = self._parse_retry_after_header(response_headers) if retry_after is not None and 0 < retry_after <= 60: return retry_after # Also cap retry count to 1000 to avoid any potential overflows with `pow` nb_retries = min(max_retries - remaining_retries, 1000) # Apply exponential backoff, but not more than the max. sleep_seconds = min(INITIAL_RETRY_DELAY * pow(2.0, nb_retries), MAX_RETRY_DELAY) # Apply some jitter, plus-or-minus half a second. jitter = 1 - 0.25 * random() timeout = sleep_seconds * jitter return timeout if timeout >= 0 else 0 def _should_retry(self, response: httpx.Response) -> bool: # Note: this is not a standard header should_retry_header = response.headers.get("x-should-retry") # If the server explicitly says whether or not to retry, obey. if should_retry_header == "true": log.debug("Retrying as header `x-should-retry` is set to `true`") return True if should_retry_header == "false": log.debug("Not retrying as header `x-should-retry` is set to `false`") return False # Retry on request timeouts. if response.status_code == 408: log.debug("Retrying due to status code %i", response.status_code) return True # Retry on lock timeouts. if response.status_code == 409: log.debug("Retrying due to status code %i", response.status_code) return True # Retry on rate limits. if response.status_code == 429: log.debug("Retrying due to status code %i", response.status_code) return True # Retry internal errors. if response.status_code >= 500: log.debug("Retrying due to status code %i", response.status_code) return True log.debug("Not retrying") return False def _should_retry_exception(self, err: BaseException) -> tuple[bool, httpx.Response | None]: """Whether an exception raised by a request attempt should be retried. Also returns the HTTP response behind the failure, when there is one, so retry timing can honor `retry-after` headers. Exceptions raised by middleware propagate to the caller as-is, except the ones that opt into the retry policy by type: `APIStatusError` (subject to the usual status-code policy), `APIConnectionError` / `APITimeoutError`, and `RetryableError`. The check walks each error's `__cause__` chain, so wrapping a retryable failure via `raise ... from err` preserves retries. """ seen: set[int] = set() current: BaseException | None = err while current is not None and id(current) not in seen: seen.add(id(current)) if isinstance(current, RetryableError): return True, None if isinstance(current, APIStatusError): return self._should_retry(current.response), current.response if isinstance(current, APIConnectionError): return True, None current = current.__cause__ return False, None def _idempotency_key(self) -> str: return f"stainless-python-retry-{uuid.uuid4()}" class _DefaultHttpxClient(httpx.Client): def __init__(self, **kwargs: Any) -> None: kwargs.setdefault("timeout", DEFAULT_TIMEOUT) kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) kwargs.setdefault("follow_redirects", True) if "transport" not in kwargs: socket_options: List[Tuple[int, int, Union[int, bool]]] = [(socket.SOL_SOCKET, socket.SO_KEEPALIVE, True)] TCP_KEEPINTVL = getattr(socket, "TCP_KEEPINTVL", None) if TCP_KEEPINTVL is not None: socket_options.append((socket.IPPROTO_TCP, TCP_KEEPINTVL, 60)) elif sys.platform == "darwin": TCP_KEEPALIVE = getattr(socket, "TCP_KEEPALIVE", 0x10) socket_options.append((socket.IPPROTO_TCP, TCP_KEEPALIVE, 60)) TCP_KEEPCNT = getattr(socket, "TCP_KEEPCNT", None) if TCP_KEEPCNT is not None: socket_options.append((socket.IPPROTO_TCP, TCP_KEEPCNT, 5)) TCP_KEEPIDLE = getattr(socket, "TCP_KEEPIDLE", None) if TCP_KEEPIDLE is not None: socket_options.append((socket.IPPROTO_TCP, TCP_KEEPIDLE, 60)) proxy_map = {key: None if url is None else Proxy(url=url) for key, url in get_environment_proxies().items()} transport_kwargs = { arg: kwargs[arg] for arg in ("verify", "cert", "trust_env", "http1", "http2", "limits") if arg in kwargs } transport_kwargs["socket_options"] = socket_options proxy_mounts = { key: None if proxy is None else HTTPTransport(proxy=proxy, **transport_kwargs) for key, proxy in proxy_map.items() } default_transport = HTTPTransport(**transport_kwargs) # Prioritize the mounts set by the user over the environment variables. proxy_mounts.update(kwargs.get("mounts", {})) kwargs["mounts"] = proxy_mounts # Sets the default transport so that HTTPX won't automatically configure proxies. kwargs["transport"] = default_transport super().__init__(**kwargs) if TYPE_CHECKING: DefaultHttpxClient = httpx.Client """An alias to `httpx.Client` that provides the same defaults that this SDK uses internally. This is useful because overriding the `http_client` with your own instance of `httpx.Client` will result in httpx's defaults being used, not ours. """ else: DefaultHttpxClient = _DefaultHttpxClient class SyncHttpxClientWrapper(DefaultHttpxClient): def __del__(self) -> None: if self.is_closed: return try: self.close() except Exception: pass class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]): _client: httpx.Client _default_stream_cls: type[Stream[Any]] | None = None _middleware_chain: CallNext | None = None webhook_key: str | None = None def __init__( self, *, version: str, base_url: str | URL, max_retries: int = DEFAULT_MAX_RETRIES, timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.Client | None = None, custom_headers: Mapping[str, str] | None = None, custom_query: Mapping[str, object] | None = None, middleware: Sequence[MiddlewareInput] | None = None, _strict_response_validation: bool, ) -> None: if not is_given(timeout): # if the user passed in a custom http client with a non-default # timeout set then we use that timeout. # # note: there is an edge case here where the user passes in a client # where they've explicitly set the timeout to match the default timeout # as this check is structural, meaning that we'll think they didn't # pass in a timeout and will ignore it if http_client and http_client.timeout != HTTPX_DEFAULT_TIMEOUT: timeout = http_client.timeout else: timeout = DEFAULT_TIMEOUT if http_client is not None and not isinstance(http_client, httpx.Client): # pyright: ignore[reportUnnecessaryIsInstance] raise TypeError( f"Invalid `http_client` argument; Expected an instance of `httpx.Client` but got {type(http_client)}" ) # materialize the middleware before validating it so that passing an # iterator/generator doesn't result in validation consuming it and the # middleware silently never running middleware = tuple(middleware or ()) if middleware: validate_sync_middleware(middleware) super().__init__( version=version, # cast to a valid type because mypy doesn't understand our type narrowing timeout=cast(Timeout, timeout), base_url=base_url, max_retries=max_retries, custom_query=custom_query, custom_headers=custom_headers, middleware=middleware, _strict_response_validation=_strict_response_validation, ) self._middleware_chain = self._build_middleware_chain() self._client = http_client or SyncHttpxClientWrapper( base_url=base_url, # cast to a valid type because mypy doesn't understand our type narrowing timeout=cast(Timeout, timeout), ) def is_closed(self) -> bool: return self._client.is_closed def close(self) -> None: """Close the underlying HTTPX client. The client will *not* be usable after this. """ # If an error is thrown while constructing a client, self._client # may not be present if hasattr(self, "_client"): self._client.close() def __enter__(self: _T) -> _T: return self def __exit__( self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None, ) -> None: self.close() def _prepare_options( self, options: FinalRequestOptions, # noqa: ARG002 ) -> FinalRequestOptions: """Hook for mutating the given options""" return options def _prepare_request( self, request: httpx.Request, # noqa: ARG002 ) -> None: """This method is used as a callback for mutating the `Request` object after it has been constructed. This is useful for cases where you want to add certain headers based off of the request properties, e.g. `url`, `method` etc. """ return None @overload def request( self, cast_to: Type[ResponseT], options: FinalRequestOptions, *, stream: Literal[True], stream_cls: Type[_StreamT], ) -> _StreamT: ... @overload def request( self, cast_to: Type[ResponseT], options: FinalRequestOptions, *, stream: Literal[False] = False, ) -> ResponseT: ... @overload def request( self, cast_to: Type[ResponseT], options: FinalRequestOptions, *, stream: bool = False, stream_cls: Type[_StreamT] | None = None, ) -> ResponseT | _StreamT: ... def request( self, cast_to: Type[ResponseT], options: FinalRequestOptions, *, stream: bool = False, stream_cls: type[_StreamT] | None = None, ) -> ResponseT | _StreamT: cast_to = self._maybe_override_cast_to(cast_to, options) # create a copy of the options we were given so that if the # options are mutated later & we then retry, the retries are # given the original options input_options = model_copy(options) if input_options.idempotency_key is None and input_options.method.lower() != "get": # ensure the idempotency key is reused between requests input_options.idempotency_key = self._idempotency_key() chain = self._middleware_chain max_retries = input_options.get_max_retries(self.max_retries) for retries_taken in range(max_retries + 1): remaining_retries = max_retries - retries_taken try: if chain is None: response, prepared = self._attempt_request( model_copy(input_options), stream=stream, retries_taken=retries_taken ) if response.is_success: return self._process_response( cast_to=cast_to, options=prepared, response=response, stream=stream, stream_cls=stream_cls, retries_taken=retries_taken, ) else: request = APIRequest( options=model_copy(input_options), cast_to=cast_to, stream=stream, stream_cls=stream_cls, retries_taken=retries_taken, ) result: Any = chain(request) if not isinstance(result, BaseAPIResponse) or result.http_response.is_success: return cast(Union[ResponseT, _StreamT], self._finalize_middleware_result(result, request)) response = result.http_response except Exception as err: should_retry, failed_response = self._should_retry_exception(err) if remaining_retries <= 0 or not should_retry: raise if failed_response is not None and not failed_response.is_closed: failed_response.close() self._sleep_for_retry( retries_taken=retries_taken, max_retries=max_retries, options=input_options, response=failed_response, ) continue # the attempt produced an error-status response — possibly inspected, # replaced or passed through by middleware if remaining_retries > 0 and self._should_retry(response): if not response.is_closed: response.close() self._sleep_for_retry( retries_taken=retries_taken, max_retries=max_retries, options=input_options, response=response, ) continue # If the response is streamed then we need to explicitly read the response # to completion before attempting to access the response text. if not response.is_closed: response.read() raise self._make_status_error_from_response(response) from None raise RuntimeError("could not resolve response (should never happen)") def _build_middleware_chain(self) -> CallNext | None: """Build the middleware invocation chain. The chain only depends on the immutable `self._middleware` tuple so it is built once at construction time. It is invoked once per HTTP attempt, inside the SDK's retry loop, with that attempt's `APIRequest`. """ if not self._middleware: return None def base_handler(req: APIRequest) -> APIResponse[Any]: options = _prepare_middleware_options(req) response, prepared = self._attempt_request(options, stream=req.stream, retries_taken=req.retries_taken) return cast( "APIResponse[Any]", self._process_response( cast_to=req.cast_to, options=prepared, response=response, stream=req.stream, stream_cls=req.stream_cls, retries_taken=req.retries_taken, ), ) def wrap(middleware: MiddlewareInput, call_next: CallNext) -> CallNext: handler = middleware.handle if isinstance(middleware, Middleware) else cast(MiddlewareCallable, middleware) def handle(req: APIRequest) -> APIResponse[Any]: return handler(req, call_next) return handle chain: CallNext = base_handler for entry in reversed(self._middleware): chain = wrap(entry, chain) return chain def _finalize_middleware_result(self, result: Any, request: APIRequest) -> Any: """Convert the `APIResponse` returned by the middleware chain into the value the original caller expects. The middleware chain operates on `APIResponse` objects; the original caller may have asked for a parsed model (the default), a `LegacyAPIResponse` (`.with_raw_response`) or the `APIResponse` wrapper itself (`.with_streaming_response` / a `cast_to` that is itself a response class). """ if not isinstance(result, BaseAPIResponse): # the middleware short-circuited with an already materialized value # (e.g. a cached model); hand it back verbatim return result response = cast("APIResponse[Any]", result) entry_mode = _middleware_entry_mode(request) if entry_mode == "raw" or entry_mode == "stream": # the original caller expects the `APIResponse` wrapper itself return response if entry_mode == "true": # `.with_raw_response` callers expect a `LegacyAPIResponse` return self._convert_to_legacy_response(response) if _expects_response_wrapper(request.cast_to): return response return response.parse() def _attempt_request( self, options: FinalRequestOptions, *, stream: bool = False, retries_taken: int = 0, ) -> tuple[httpx.Response, FinalRequestOptions]: """Prepare, build and send a single HTTP attempt. Returns the response regardless of its status code, along with the prepared options it was sent with — status handling (the retry policy, raising typed errors for the caller) happens above, where middleware can inspect error responses first. Connection failures raise typed errors (`APITimeoutError`, `APIConnectionError`). """ options = self._prepare_options(options) request = self._build_request(options, retries_taken=retries_taken) self._prepare_request(request) kwargs: HttpxSendArgs = {} if self.custom_auth is not None: kwargs["auth"] = self.custom_auth if options.follow_redirects is not None: kwargs["follow_redirects"] = options.follow_redirects log.debug("Sending HTTP Request: %s %s", request.method, request.url) try: response = self._client.send( request, stream=stream or self._should_stream_response_body(request=request), **kwargs, ) except httpx.TimeoutException as err: log.debug("Encountered httpx.TimeoutException", exc_info=True) raise APITimeoutError(request=request) from err except Exception as err: if isinstance(err, AnthropicError): # SDK-originated errors already carry their own type; don't wrap. raise log.debug("Encountered Exception", exc_info=True) raise APIConnectionError(request=request) from err log.debug( 'HTTP Response: %s %s "%i %s" %s', request.method, request.url, response.status_code, response.reason_phrase, response.headers, ) log.debug("request_id: %s", response.headers.get("request-id")) return response, options def _sleep_for_retry( self, *, retries_taken: int, max_retries: int, options: FinalRequestOptions, response: httpx.Response | None ) -> None: remaining_retries = max_retries - retries_taken if remaining_retries == 1: log.debug("1 retry left") else: log.debug("%i retries left", remaining_retries) timeout = self._calculate_retry_timeout(remaining_retries, options, response.headers if response else None) log.info("Retrying request to %s in %f seconds", options.url, timeout) time.sleep(timeout) def _process_response( self, *, cast_to: Type[ResponseT], options: FinalRequestOptions, response: httpx.Response, stream: bool, stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, retries_taken: int = 0, ) -> ResponseT: if response.request.headers.get(RAW_RESPONSE_HEADER) == "true": return cast( ResponseT, LegacyAPIResponse( raw=response, client=self, cast_to=cast_to, stream=stream, stream_cls=stream_cls, options=options, retries_taken=retries_taken, ), ) origin = get_origin(cast_to) or cast_to if ( inspect.isclass(origin) and issubclass(origin, BaseAPIResponse) # we only want to actually return the custom BaseAPIResponse class if we're # returning the raw response, or if we're not streaming SSE, as if we're streaming # SSE then `cast_to` doesn't actively reflect the type we need to parse into and (not stream or bool(response.request.headers.get(RAW_RESPONSE_HEADER))) ): if not issubclass(origin, APIResponse): raise TypeError(f"API Response types must subclass {APIResponse}; Received {origin}") response_cls = cast("type[BaseAPIResponse[Any]]", cast_to) return cast( ResponseT, response_cls( raw=response, client=self, cast_to=extract_response_type(response_cls), stream=stream, stream_cls=stream_cls, options=options, retries_taken=retries_taken, ), ) if cast_to == httpx.Response: return cast(ResponseT, response) api_response = APIResponse( raw=response, client=self, cast_to=cast("type[ResponseT]", cast_to), # pyright: ignore[reportUnnecessaryCast] stream=stream, stream_cls=stream_cls, options=options, retries_taken=retries_taken, ) if bool(response.request.headers.get(RAW_RESPONSE_HEADER)): return cast(ResponseT, api_response) return api_response.parse() def _request_api_list( self, model: Type[object], page: Type[SyncPageT], options: FinalRequestOptions, ) -> SyncPageT: def _parser(resp: SyncPageT) -> SyncPageT: resp._set_private_attributes( client=self, model=model, options=options, ) return resp options.post_parser = _parser return self.request(page, options, stream=False) @overload def get( self, path: str, *, cast_to: Type[ResponseT], options: RequestOptions = {}, stream: Literal[False] = False, ) -> ResponseT: ... @overload def get( self, path: str, *, cast_to: Type[ResponseT], options: RequestOptions = {}, stream: Literal[True], stream_cls: type[_StreamT], ) -> _StreamT: ... @overload def get( self, path: str, *, cast_to: Type[ResponseT], options: RequestOptions = {}, stream: bool, stream_cls: type[_StreamT] | None = None, ) -> ResponseT | _StreamT: ... def get( self, path: str, *, cast_to: Type[ResponseT], options: RequestOptions = {}, stream: bool = False, stream_cls: type[_StreamT] | None = None, ) -> ResponseT | _StreamT: opts = FinalRequestOptions.construct(method="get", url=path, **options) # cast is required because mypy complains about returning Any even though # it understands the type variables return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)) @overload def post( self, path: str, *, cast_to: Type[ResponseT], body: Body | None = None, content: BinaryTypes | None = None, options: RequestOptions = {}, files: RequestFiles | None = None, stream: Literal[False] = False, ) -> ResponseT: ... @overload def post( self, path: str, *, cast_to: Type[ResponseT], body: Body | None = None, content: BinaryTypes | None = None, options: RequestOptions = {}, files: RequestFiles | None = None, stream: Literal[True], stream_cls: type[_StreamT], ) -> _StreamT: ... @overload def post( self, path: str, *, cast_to: Type[ResponseT], body: Body | None = None, content: BinaryTypes | None = None, options: RequestOptions = {}, files: RequestFiles | None = None, stream: bool, stream_cls: type[_StreamT] | None = None, ) -> ResponseT | _StreamT: ... def post( self, path: str, *, cast_to: Type[ResponseT], body: Body | None = None, content: BinaryTypes | None = None, options: RequestOptions = {}, files: RequestFiles | None = None, stream: bool = False, stream_cls: type[_StreamT] | None = None, ) -> ResponseT | _StreamT: if body is not None and content is not None: raise TypeError("Passing both `body` and `content` is not supported") if files is not None and content is not None: raise TypeError("Passing both `files` and `content` is not supported") if isinstance(body, bytes): warnings.warn( "Passing raw bytes as `body` is deprecated and will be removed in a future version. " "Please pass raw bytes via the `content` parameter instead.", DeprecationWarning, stacklevel=2, ) opts = FinalRequestOptions.construct( method="post", url=path, json_data=body, content=content, files=to_httpx_files(files), **options ) return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)) def patch( self, path: str, *, cast_to: Type[ResponseT], body: Body | None = None, content: BinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, ) -> ResponseT: if body is not None and content is not None: raise TypeError("Passing both `body` and `content` is not supported") if files is not None and content is not None: raise TypeError("Passing both `files` and `content` is not supported") if isinstance(body, bytes): warnings.warn( "Passing raw bytes as `body` is deprecated and will be removed in a future version. " "Please pass raw bytes via the `content` parameter instead.", DeprecationWarning, stacklevel=2, ) opts = FinalRequestOptions.construct( method="patch", url=path, json_data=body, content=content, files=to_httpx_files(files), **options ) return self.request(cast_to, opts) def put( self, path: str, *, cast_to: Type[ResponseT], body: Body | None = None, content: BinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, ) -> ResponseT: if body is not None and content is not None: raise TypeError("Passing both `body` and `content` is not supported") if files is not None and content is not None: raise TypeError("Passing both `files` and `content` is not supported") if isinstance(body, bytes): warnings.warn( "Passing raw bytes as `body` is deprecated and will be removed in a future version. " "Please pass raw bytes via the `content` parameter instead.", DeprecationWarning, stacklevel=2, ) opts = FinalRequestOptions.construct( method="put", url=path, json_data=body, content=content, files=to_httpx_files(files), **options ) return self.request(cast_to, opts) def delete( self, path: str, *, cast_to: Type[ResponseT], body: Body | None = None, content: BinaryTypes | None = None, options: RequestOptions = {}, ) -> ResponseT: if body is not None and content is not None: raise TypeError("Passing both `body` and `content` is not supported") if isinstance(body, bytes): warnings.warn( "Passing raw bytes as `body` is deprecated and will be removed in a future version. " "Please pass raw bytes via the `content` parameter instead.", DeprecationWarning, stacklevel=2, ) opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, content=content, **options) return self.request(cast_to, opts) def get_api_list( self, path: str, *, model: Type[object], page: Type[SyncPageT], body: Body | None = None, options: RequestOptions = {}, method: str = "get", ) -> SyncPageT: opts = FinalRequestOptions.construct(method=method, url=path, json_data=body, **options) return self._request_api_list(model, page, opts) class _DefaultAsyncHttpxClient(httpx.AsyncClient): def __init__(self, **kwargs: Any) -> None: kwargs.setdefault("timeout", DEFAULT_TIMEOUT) kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) kwargs.setdefault("follow_redirects", True) if "transport" not in kwargs: socket_options: List[Tuple[int, int, Union[int, bool]]] = [(socket.SOL_SOCKET, socket.SO_KEEPALIVE, True)] TCP_KEEPINTVL = getattr(socket, "TCP_KEEPINTVL", None) if TCP_KEEPINTVL is not None: socket_options.append((socket.IPPROTO_TCP, TCP_KEEPINTVL, 60)) elif sys.platform == "darwin": TCP_KEEPALIVE = getattr(socket, "TCP_KEEPALIVE", 0x10) socket_options.append((socket.IPPROTO_TCP, TCP_KEEPALIVE, 60)) TCP_KEEPCNT = getattr(socket, "TCP_KEEPCNT", None) if TCP_KEEPCNT is not None: socket_options.append((socket.IPPROTO_TCP, TCP_KEEPCNT, 5)) TCP_KEEPIDLE = getattr(socket, "TCP_KEEPIDLE", None) if TCP_KEEPIDLE is not None: socket_options.append((socket.IPPROTO_TCP, TCP_KEEPIDLE, 60)) proxy_map = {key: None if url is None else Proxy(url=url) for key, url in get_environment_proxies().items()} transport_kwargs = { arg: kwargs[arg] for arg in ("verify", "cert", "trust_env", "http1", "http2", "limits") if arg in kwargs } transport_kwargs["socket_options"] = socket_options proxy_mounts = { key: None if proxy is None else AsyncHTTPTransport(proxy=proxy, **transport_kwargs) for key, proxy in proxy_map.items() } default_transport = AsyncHTTPTransport(**transport_kwargs) # Prioritize the mounts set by the user over the environment variables. proxy_mounts.update(kwargs.get("mounts", {})) kwargs["mounts"] = proxy_mounts # Sets the default transport so that HTTPX won't automatically configure proxies. kwargs["transport"] = default_transport super().__init__(**kwargs) try: import httpx_aiohttp except ImportError: class _DefaultAioHttpClient(httpx.AsyncClient): def __init__(self, **_kwargs: Any) -> None: raise RuntimeError("To use the aiohttp client you must have installed the package with the `aiohttp` extra") else: class _DefaultAioHttpClient(httpx_aiohttp.HttpxAiohttpClient): # type: ignore def __init__(self, **kwargs: Any) -> None: kwargs.setdefault("timeout", DEFAULT_TIMEOUT) kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) kwargs.setdefault("follow_redirects", True) super().__init__(**kwargs) if TYPE_CHECKING: DefaultAsyncHttpxClient = httpx.AsyncClient """An alias to `httpx.AsyncClient` that provides the same defaults that this SDK uses internally. This is useful because overriding the `http_client` with your own instance of `httpx.AsyncClient` will result in httpx's defaults being used, not ours. """ DefaultAioHttpClient = httpx.AsyncClient """An alias to `httpx.AsyncClient` that changes the default HTTP transport to `aiohttp`.""" else: DefaultAsyncHttpxClient = _DefaultAsyncHttpxClient DefaultAioHttpClient = _DefaultAioHttpClient class AsyncHttpxClientWrapper(DefaultAsyncHttpxClient): def __del__(self) -> None: if self.is_closed: return try: # TODO(someday): support non asyncio runtimes here asyncio.get_running_loop().create_task(self.aclose()) except Exception: pass class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]): _client: httpx.AsyncClient _default_stream_cls: type[AsyncStream[Any]] | None = None _middleware_chain: AsyncCallNext | None = None webhook_key: str | None = None def __init__( self, *, version: str, base_url: str | URL, _strict_response_validation: bool, max_retries: int = DEFAULT_MAX_RETRIES, timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.AsyncClient | None = None, custom_headers: Mapping[str, str] | None = None, custom_query: Mapping[str, object] | None = None, middleware: Sequence[MiddlewareInput] | None = None, ) -> None: if not is_given(timeout): # if the user passed in a custom http client with a non-default # timeout set then we use that timeout. # # note: there is an edge case here where the user passes in a client # where they've explicitly set the timeout to match the default timeout # as this check is structural, meaning that we'll think they didn't # pass in a timeout and will ignore it if http_client and http_client.timeout != HTTPX_DEFAULT_TIMEOUT: timeout = http_client.timeout else: timeout = DEFAULT_TIMEOUT if http_client is not None and not isinstance(http_client, httpx.AsyncClient): # pyright: ignore[reportUnnecessaryIsInstance] raise TypeError( f"Invalid `http_client` argument; Expected an instance of `httpx.AsyncClient` but got {type(http_client)}" ) # materialize the middleware before validating it so that passing an # iterator/generator doesn't result in validation consuming it and the # middleware silently never running middleware = tuple(middleware or ()) if middleware: validate_async_middleware(middleware) super().__init__( version=version, base_url=base_url, # cast to a valid type because mypy doesn't understand our type narrowing timeout=cast(Timeout, timeout), max_retries=max_retries, custom_query=custom_query, custom_headers=custom_headers, middleware=middleware, _strict_response_validation=_strict_response_validation, ) self._middleware_chain = self._build_middleware_chain() self._client = http_client or AsyncHttpxClientWrapper( base_url=base_url, # cast to a valid type because mypy doesn't understand our type narrowing timeout=cast(Timeout, timeout), ) def is_closed(self) -> bool: return self._client.is_closed async def close(self) -> None: """Close the underlying HTTPX client. The client will *not* be usable after this. """ await self._client.aclose() async def __aenter__(self: _T) -> _T: return self async def __aexit__( self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None, ) -> None: await self.close() async def _prepare_options( self, options: FinalRequestOptions, # noqa: ARG002 ) -> FinalRequestOptions: """Hook for mutating the given options""" return options async def _prepare_request( self, request: httpx.Request, # noqa: ARG002 ) -> None: """This method is used as a callback for mutating the `Request` object after it has been constructed. This is useful for cases where you want to add certain headers based off of the request properties, e.g. `url`, `method` etc. """ return None @overload async def request( self, cast_to: Type[ResponseT], options: FinalRequestOptions, *, stream: Literal[False] = False, ) -> ResponseT: ... @overload async def request( self, cast_to: Type[ResponseT], options: FinalRequestOptions, *, stream: Literal[True], stream_cls: type[_AsyncStreamT], ) -> _AsyncStreamT: ... @overload async def request( self, cast_to: Type[ResponseT], options: FinalRequestOptions, *, stream: bool, stream_cls: type[_AsyncStreamT] | None = None, ) -> ResponseT | _AsyncStreamT: ... async def request( self, cast_to: Type[ResponseT], options: FinalRequestOptions, *, stream: bool = False, stream_cls: type[_AsyncStreamT] | None = None, ) -> ResponseT | _AsyncStreamT: if self._platform is None: # `get_platform` can make blocking IO calls so we # execute it earlier while we are in an async context self._platform = await asyncify(get_platform)() cast_to = self._maybe_override_cast_to(cast_to, options) # create a copy of the options we were given so that if the # options are mutated later & we then retry, the retries are # given the original options input_options = model_copy(options) if input_options.idempotency_key is None and input_options.method.lower() != "get": # ensure the idempotency key is reused between requests input_options.idempotency_key = self._idempotency_key() chain = self._middleware_chain max_retries = input_options.get_max_retries(self.max_retries) for retries_taken in range(max_retries + 1): remaining_retries = max_retries - retries_taken try: if chain is None: response, prepared = await self._attempt_request( model_copy(input_options), stream=stream, retries_taken=retries_taken ) if response.is_success: return await self._process_response( cast_to=cast_to, options=prepared, response=response, stream=stream, stream_cls=stream_cls, retries_taken=retries_taken, ) else: request = APIRequest( options=model_copy(input_options), cast_to=cast_to, stream=stream, stream_cls=stream_cls, retries_taken=retries_taken, ) result: Any = await chain(request) if not isinstance(result, BaseAPIResponse) or result.http_response.is_success: return cast( Union[ResponseT, _AsyncStreamT], await self._finalize_middleware_result(result, request) ) response = result.http_response except Exception as err: should_retry, failed_response = self._should_retry_exception(err) if remaining_retries <= 0 or not should_retry: raise if failed_response is not None and not failed_response.is_closed: await failed_response.aclose() await self._sleep_for_retry( retries_taken=retries_taken, max_retries=max_retries, options=input_options, response=failed_response, ) continue # the attempt produced an error-status response — possibly inspected, # replaced or passed through by middleware if remaining_retries > 0 and self._should_retry(response): if not response.is_closed: await response.aclose() await self._sleep_for_retry( retries_taken=retries_taken, max_retries=max_retries, options=input_options, response=response, ) continue # If the response is streamed then we need to explicitly read the response # to completion before attempting to access the response text. if not response.is_closed: await response.aread() raise self._make_status_error_from_response(response) from None raise RuntimeError("could not resolve response (should never happen)") def _build_middleware_chain(self) -> AsyncCallNext | None: """Build the middleware invocation chain. The chain only depends on the immutable `self._middleware` tuple so it is built once at construction time. It is invoked once per HTTP attempt, inside the SDK's retry loop, with that attempt's `APIRequest`. """ if not self._middleware: return None async def base_handler(req: APIRequest) -> AsyncAPIResponse[Any]: options = _prepare_middleware_options(req) response, prepared = await self._attempt_request( options, stream=req.stream, retries_taken=req.retries_taken ) return cast( "AsyncAPIResponse[Any]", await self._process_response( cast_to=req.cast_to, options=prepared, response=response, stream=req.stream, stream_cls=req.stream_cls, retries_taken=req.retries_taken, ), ) def wrap(middleware: MiddlewareInput, call_next: AsyncCallNext) -> AsyncCallNext: handler = ( middleware.handle_async if isinstance(middleware, Middleware) else cast(AsyncMiddlewareCallable, middleware) ) async def handle(req: APIRequest) -> AsyncAPIResponse[Any]: return await handler(req, call_next) return handle chain: AsyncCallNext = base_handler for entry in reversed(self._middleware): chain = wrap(entry, chain) return chain async def _finalize_middleware_result(self, result: Any, request: APIRequest) -> Any: """Convert the `AsyncAPIResponse` returned by the middleware chain into the value the original caller expects. The middleware chain operates on `AsyncAPIResponse` objects; the original caller may have asked for a parsed model (the default), a `LegacyAPIResponse` (`.with_raw_response`) or the `AsyncAPIResponse` wrapper itself (`.with_streaming_response` / a `cast_to` that is itself a response class). """ if not isinstance(result, BaseAPIResponse): # the middleware short-circuited with an already materialized value # (e.g. a cached model); hand it back verbatim return result response = cast("AsyncAPIResponse[Any]", result) entry_mode = _middleware_entry_mode(request) if entry_mode == "raw" or entry_mode == "stream": # the original caller expects the `AsyncAPIResponse` wrapper itself return response if entry_mode == "true": # `.with_raw_response` callers expect a `LegacyAPIResponse` return self._convert_to_legacy_response(response) if _expects_response_wrapper(request.cast_to): return response return await response.parse() async def _attempt_request( self, options: FinalRequestOptions, *, stream: bool = False, retries_taken: int = 0, ) -> tuple[httpx.Response, FinalRequestOptions]: """Prepare, build and send a single HTTP attempt. Returns the response regardless of its status code, along with the prepared options it was sent with — status handling (the retry policy, raising typed errors for the caller) happens above, where middleware can inspect error responses first. Connection failures raise typed errors (`APITimeoutError`, `APIConnectionError`). """ options = await self._prepare_options(options) request = self._build_request(options, retries_taken=retries_taken) await self._prepare_request(request) kwargs: HttpxSendArgs = {} if self.custom_auth is not None: kwargs["auth"] = self.custom_auth if options.follow_redirects is not None: kwargs["follow_redirects"] = options.follow_redirects log.debug("Sending HTTP Request: %s %s", request.method, request.url) try: response = await self._client.send( request, stream=stream or self._should_stream_response_body(request=request), **kwargs, ) except httpx.TimeoutException as err: log.debug("Encountered httpx.TimeoutException", exc_info=True) raise APITimeoutError(request=request) from err except Exception as err: if isinstance(err, AnthropicError): # SDK-originated errors already carry their own type; don't wrap. raise log.debug("Encountered Exception", exc_info=True) raise APIConnectionError(request=request) from err log.debug( 'HTTP Response: %s %s "%i %s" %s', request.method, request.url, response.status_code, response.reason_phrase, response.headers, ) log.debug("request_id: %s", response.headers.get("request-id")) return response, options async def _sleep_for_retry( self, *, retries_taken: int, max_retries: int, options: FinalRequestOptions, response: httpx.Response | None ) -> None: remaining_retries = max_retries - retries_taken if remaining_retries == 1: log.debug("1 retry left") else: log.debug("%i retries left", remaining_retries) timeout = self._calculate_retry_timeout(remaining_retries, options, response.headers if response else None) log.info("Retrying request to %s in %f seconds", options.url, timeout) await anyio.sleep(timeout) async def _process_response( self, *, cast_to: Type[ResponseT], options: FinalRequestOptions, response: httpx.Response, stream: bool, stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, retries_taken: int = 0, ) -> ResponseT: if response.request.headers.get(RAW_RESPONSE_HEADER) == "true": return cast( ResponseT, LegacyAPIResponse( raw=response, client=self, cast_to=cast_to, stream=stream, stream_cls=stream_cls, options=options, retries_taken=retries_taken, ), ) origin = get_origin(cast_to) or cast_to if ( inspect.isclass(origin) and issubclass(origin, BaseAPIResponse) # we only want to actually return the custom BaseAPIResponse class if we're # returning the raw response, or if we're not streaming SSE, as if we're streaming # SSE then `cast_to` doesn't actively reflect the type we need to parse into and (not stream or bool(response.request.headers.get(RAW_RESPONSE_HEADER))) ): if not issubclass(origin, AsyncAPIResponse): raise TypeError(f"API Response types must subclass {AsyncAPIResponse}; Received {origin}") response_cls = cast("type[BaseAPIResponse[Any]]", cast_to) return cast( "ResponseT", response_cls( raw=response, client=self, cast_to=extract_response_type(response_cls), stream=stream, stream_cls=stream_cls, options=options, retries_taken=retries_taken, ), ) if cast_to == httpx.Response: return cast(ResponseT, response) api_response = AsyncAPIResponse( raw=response, client=self, cast_to=cast("type[ResponseT]", cast_to), # pyright: ignore[reportUnnecessaryCast] stream=stream, stream_cls=stream_cls, options=options, retries_taken=retries_taken, ) if bool(response.request.headers.get(RAW_RESPONSE_HEADER)): return cast(ResponseT, api_response) return await api_response.parse() def _request_api_list( self, model: Type[_T], page: Type[AsyncPageT], options: FinalRequestOptions, ) -> AsyncPaginator[_T, AsyncPageT]: return AsyncPaginator(client=self, options=options, page_cls=page, model=model) @overload async def get( self, path: str, *, cast_to: Type[ResponseT], options: RequestOptions = {}, stream: Literal[False] = False, ) -> ResponseT: ... @overload async def get( self, path: str, *, cast_to: Type[ResponseT], options: RequestOptions = {}, stream: Literal[True], stream_cls: type[_AsyncStreamT], ) -> _AsyncStreamT: ... @overload async def get( self, path: str, *, cast_to: Type[ResponseT], options: RequestOptions = {}, stream: bool, stream_cls: type[_AsyncStreamT] | None = None, ) -> ResponseT | _AsyncStreamT: ... async def get( self, path: str, *, cast_to: Type[ResponseT], options: RequestOptions = {}, stream: bool = False, stream_cls: type[_AsyncStreamT] | None = None, ) -> ResponseT | _AsyncStreamT: opts = FinalRequestOptions.construct(method="get", url=path, **options) return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls) @overload async def post( self, path: str, *, cast_to: Type[ResponseT], body: Body | None = None, content: AsyncBinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, stream: Literal[False] = False, ) -> ResponseT: ... @overload async def post( self, path: str, *, cast_to: Type[ResponseT], body: Body | None = None, content: AsyncBinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, stream: Literal[True], stream_cls: type[_AsyncStreamT], ) -> _AsyncStreamT: ... @overload async def post( self, path: str, *, cast_to: Type[ResponseT], body: Body | None = None, content: AsyncBinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, stream: bool, stream_cls: type[_AsyncStreamT] | None = None, ) -> ResponseT | _AsyncStreamT: ... async def post( self, path: str, *, cast_to: Type[ResponseT], body: Body | None = None, content: AsyncBinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, stream: bool = False, stream_cls: type[_AsyncStreamT] | None = None, ) -> ResponseT | _AsyncStreamT: if body is not None and content is not None: raise TypeError("Passing both `body` and `content` is not supported") if files is not None and content is not None: raise TypeError("Passing both `files` and `content` is not supported") if isinstance(body, bytes): warnings.warn( "Passing raw bytes as `body` is deprecated and will be removed in a future version. " "Please pass raw bytes via the `content` parameter instead.", DeprecationWarning, stacklevel=2, ) opts = FinalRequestOptions.construct( method="post", url=path, json_data=body, content=content, files=await async_to_httpx_files(files), **options ) return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls) async def patch( self, path: str, *, cast_to: Type[ResponseT], body: Body | None = None, content: AsyncBinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, ) -> ResponseT: if body is not None and content is not None: raise TypeError("Passing both `body` and `content` is not supported") if files is not None and content is not None: raise TypeError("Passing both `files` and `content` is not supported") if isinstance(body, bytes): warnings.warn( "Passing raw bytes as `body` is deprecated and will be removed in a future version. " "Please pass raw bytes via the `content` parameter instead.", DeprecationWarning, stacklevel=2, ) opts = FinalRequestOptions.construct( method="patch", url=path, json_data=body, content=content, files=await async_to_httpx_files(files), **options, ) return await self.request(cast_to, opts) async def put( self, path: str, *, cast_to: Type[ResponseT], body: Body | None = None, content: AsyncBinaryTypes | None = None, files: RequestFiles | None = None, options: RequestOptions = {}, ) -> ResponseT: if body is not None and content is not None: raise TypeError("Passing both `body` and `content` is not supported") if files is not None and content is not None: raise TypeError("Passing both `files` and `content` is not supported") if isinstance(body, bytes): warnings.warn( "Passing raw bytes as `body` is deprecated and will be removed in a future version. " "Please pass raw bytes via the `content` parameter instead.", DeprecationWarning, stacklevel=2, ) opts = FinalRequestOptions.construct( method="put", url=path, json_data=body, content=content, files=await async_to_httpx_files(files), **options ) return await self.request(cast_to, opts) async def delete( self, path: str, *, cast_to: Type[ResponseT], body: Body | None = None, content: AsyncBinaryTypes | None = None, options: RequestOptions = {}, ) -> ResponseT: if body is not None and content is not None: raise TypeError("Passing both `body` and `content` is not supported") if isinstance(body, bytes): warnings.warn( "Passing raw bytes as `body` is deprecated and will be removed in a future version. " "Please pass raw bytes via the `content` parameter instead.", DeprecationWarning, stacklevel=2, ) opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, content=content, **options) return await self.request(cast_to, opts) def get_api_list( self, path: str, *, model: Type[_T], page: Type[AsyncPageT], body: Body | None = None, options: RequestOptions = {}, method: str = "get", ) -> AsyncPaginator[_T, AsyncPageT]: opts = FinalRequestOptions.construct(method=method, url=path, json_data=body, **options) return self._request_api_list(model, page, opts) def make_request_options( *, query: Query | None = None, extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, idempotency_key: str | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, post_parser: PostParser | NotGiven = not_given, ) -> RequestOptions: """Create a dict of type RequestOptions without keys of NotGiven values.""" options: RequestOptions = {} if extra_headers is not None: options["headers"] = extra_headers if extra_body is not None: options["extra_json"] = cast(AnyMapping, extra_body) if query is not None: options["params"] = query if extra_query is not None: options["params"] = {**options.get("params", {}), **extra_query} if not isinstance(timeout, NotGiven): options["timeout"] = timeout if idempotency_key is not None: options["idempotency_key"] = idempotency_key if is_given(post_parser): # internal options["post_parser"] = post_parser # type: ignore return options class ForceMultipartDict(Dict[str, None]): def __bool__(self) -> bool: return True class OtherPlatform: def __init__(self, name: str) -> None: self.name = name @override def __str__(self) -> str: return f"Other:{self.name}" Platform = Union[ OtherPlatform, Literal[ "MacOS", "Linux", "Windows", "FreeBSD", "OpenBSD", "iOS", "Android", "Unknown", ], ] def get_platform() -> Platform: try: system = platform.system().lower() platform_name = platform.platform().lower() except Exception: return "Unknown" if "iphone" in platform_name or "ipad" in platform_name: # Tested using Python3IDE on an iPhone 11 and Pythonista on an iPad 7 # system is Darwin and platform_name is a string like: # - Darwin-21.6.0-iPhone12,1-64bit # - Darwin-21.6.0-iPad7,11-64bit return "iOS" if system == "darwin": return "MacOS" if system == "windows": return "Windows" if "android" in platform_name: # Tested using Pydroid 3 # system is Linux and platform_name is a string like 'Linux-5.10.81-android12-9-00001-geba40aecb3b7-ab8534902-aarch64-with-libc' return "Android" if system == "linux": # https://distro.readthedocs.io/en/latest/#distro.id distro_id = distro.id() if distro_id == "freebsd": return "FreeBSD" if distro_id == "openbsd": return "OpenBSD" return "Linux" if platform_name: return OtherPlatform(platform_name) return "Unknown" @lru_cache(maxsize=None) def platform_headers(version: str, *, platform: Platform | None) -> Dict[str, str]: return { "X-Stainless-Lang": "python", "X-Stainless-Package-Version": version, "X-Stainless-OS": str(platform or get_platform()), "X-Stainless-Arch": str(get_architecture()), "X-Stainless-Runtime": get_python_runtime(), "X-Stainless-Runtime-Version": get_python_version(), } class OtherArch: def __init__(self, name: str) -> None: self.name = name @override def __str__(self) -> str: return f"other:{self.name}" Arch = Union[OtherArch, Literal["x32", "x64", "arm", "arm64", "unknown"]] def get_python_runtime() -> str: try: return platform.python_implementation() except Exception: return "unknown" def get_python_version() -> str: try: return platform.python_version() except Exception: return "unknown" def get_architecture() -> Arch: try: machine = platform.machine().lower() except Exception: return "unknown" if machine in ("arm64", "aarch64"): return "arm64" # TODO: untested if machine == "arm": return "arm" if machine == "x86_64": return "x64" # TODO: untested if sys.maxsize <= 2**32: return "x32" if machine: return OtherArch(machine) return "unknown" def _strip_omit(mapping: Mapping[_T_co, Union[_T, Omit]]) -> Dict[_T_co, _T]: """Drop entries whose value is an `Omit` removal marker.""" return {key: value for key, value in mapping.items() if not isinstance(value, Omit)} def _merge_mappings( obj1: Mapping[_T_co, Union[_T, Omit]], obj2: Mapping[_T_co, Union[_T, Omit]], ) -> Dict[_T_co, _T]: """Merge two mappings of the same type, removing any values that are instances of `Omit`. In cases with duplicate keys the second mapping takes precedence. """ return _strip_omit({**obj1, **obj2}) # Append-on-merge header support (hand-written, upstream to Stainless). # # Headers whose values accumulate across a merge instead of the later mapping's # value replacing the earlier one. When multiple mappings set one of these, the # values are concatenated into a single comma-separated value (order-preserving, # deduplicated) rather than clobbered. _APPEND_HEADERS = frozenset({"x-stainless-helper"}) def _append_header_value(existing: str, addition: str) -> str: """Append `addition` to a comma-separated header value, skipping tokens that are already present so the same helper isn't recorded twice. Values are joined with `", "`, the same format `lib._stainless_helpers` uses when it builds the `x-stainless-helper` header. """ tokens = [token for token in (raw.strip() for raw in existing.split(",")) if token] for token in (raw.strip() for raw in addition.split(",")): if token and token not in tokens: tokens.append(token) return ", ".join(tokens) def merge_headers(*mappings: Mapping[str, Union[str, Omit]]) -> Dict[str, str]: """Merge header mappings, with later mappings taking precedence on a key clash, exactly like `_merge_mappings`. The exception is the headers in `_APPEND_HEADERS`: those keys are matched case-insensitively (and stored under their lowercase form) and their string values accumulate into a single comma-separated, deduplicated value instead of the later one overriding the earlier one. `Omit` values are preserved (they mark a header for removal and are only dropped at request-build time, e.g. with `_strip_omit`); the `Dict[str, str]` return type is the same fudge the rest of the header plumbing already uses for `Omit`-bearing header mappings. """ merged: Dict[str, Union[str, Omit]] = {} for mapping in mappings: for key, value in mapping.items(): lower = key.lower() if lower not in _APPEND_HEADERS: merged[key] = value continue # Append headers are stored under their lowercase key so every # case-variant lands on (and appends to) the same entry. existing = merged.get(lower) if isinstance(existing, str) and isinstance(value, str): merged[lower] = _append_header_value(existing, value) else: # `Omit` (removal) can't take part in an append; the later # value overrides, as it does for any other header. merged[lower] = value return cast("Dict[str, str]", merged) def _middleware_entry_mode(request: APIRequest) -> Literal["raw", "stream", "true"] | None: """The raw-response mode the original caller entered the middleware chain with.""" headers = request.options.headers mode = headers.get(RAW_RESPONSE_HEADER) if is_given(headers) else None if isinstance(mode, str) and mode in ("raw", "stream", "true"): # mypy for some reason cannot narrow the type return mode # type: ignore return None def _prepare_middleware_options(request: APIRequest) -> FinalRequestOptions: """Build the options the base middleware handler hands to `_attempt_request()`. Returns a copy so that mutations made by the request pipeline never leak back into the `APIRequest` between separate `call_next(...)` invocations, with the raw-response header set so that the pipeline produces an `APIResponse` / `AsyncAPIResponse` for the middleware chain. """ options = model_copy(request.options) headers = dict(options.headers) if is_given(options.headers) else {} if headers.get(RAW_RESPONSE_HEADER) not in ("raw", "stream"): # Note: this intentionally *replaces* the `LegacyAPIResponse` mode set by # `.with_raw_response` wrappers so that the middleware chain always operates # on a true `APIResponse` / `AsyncAPIResponse`. The "stream" mode set by # `.with_streaming_response` already produces one and is left as-is so that # the response body is not eagerly read. `_finalize_middleware_result()` # restores the value the original caller expects. headers[RAW_RESPONSE_HEADER] = "raw" options.headers = headers return options def _expects_response_wrapper(cast_to: Any) -> bool: """Whether the given `cast_to` asks for the `APIResponse` wrapper itself.""" origin = get_origin(cast_to) or cast_to return inspect.isclass(origin) and issubclass(origin, BaseAPIResponse) anthropic-sdk-python-0.120.2/src/anthropic/_client.py000066400000000000000000001312631523216435200225170ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os from typing import TYPE_CHECKING, Any, Mapping, Sequence from typing_extensions import Self, override import httpx from . import _constants, _exceptions from ._qs import Querystring from ._types import ( Omit, Headers, Timeout, NotGiven, Transport, ProxiesTypes, RequestOptions, not_given, ) from ._utils import ( is_given, is_mapping_t, get_async_library, ) from ._compat import cached_property from ._version import __version__ from ._streaming import Stream as Stream, AsyncStream as AsyncStream from ._exceptions import APIStatusError from ._middleware import MiddlewareInput from ._base_client import ( DEFAULT_MAX_RETRIES, SyncAPIClient, AsyncAPIClient, merge_headers, ) # --- credentials support (hand-written, upstream to Stainless) --- from .lib.credentials import ( TokenCache, InMemoryConfig, AccessTokenAuth, CredentialsFile, AccessTokenProvider, default_credentials, ) from .lib.credentials._auth import ( warn_env_static_shadows_auto_discovery, warn_explicit_static_shadows_credentials, ) from .lib.credentials._constants import _has_auto_discoverable_credentials def _is_base_client(client: object) -> bool: """True only for the base ``Anthropic`` / ``AsyncAnthropic`` classes, not subclasses. Subclasses (``AnthropicAWS``, ``AnthropicFoundry``) have their own auth paths and must not run the credential chain or forward ``credentials`` through their ``__init__`` (which doesn't accept the kwarg). """ return type(client) in (Anthropic, AsyncAnthropic) def _close_credentials(credentials: object) -> None: """Release any resources owned by a credential provider, if it exposes ``close()``.""" close = getattr(credentials, "close", None) if close is not None: close() def _bind_credentials_base_url(credentials: AccessTokenProvider | None, base_url: str) -> None: """If the credential provider supports ``bind_base_url``, pass it the client's resolved ``base_url`` so the token exchange and API calls hit the same deployment without the caller passing the URL twice. Providers without the hook (plain callables, custom impls) are left untouched and MUST resolve their own token-exchange ``base_url`` — the client does not second-guess them. """ bind = getattr(credentials, "bind_base_url", None) if callable(bind): bind(base_url) def _warn_explicit_shadow(*, api_key: str | None, auth_token: str | None, credentials: object) -> None: """Warn when an explicit ``api_key=`` / ``auth_token=`` argument shadows an explicit ``credentials=`` provider. Call *after* any copy-inheritance merging so the params reflect the resolved values.""" if credentials is None: return if api_key is not None: warn_explicit_static_shadows_credentials("api_key") if auth_token is not None: warn_explicit_static_shadows_credentials("auth_token") def _warn_env_shadow(*, api_key: str | None, auth_token: str | None) -> None: """Warn when an ``ANTHROPIC_API_KEY`` / ``ANTHROPIC_AUTH_TOKEN`` from the environment is set alongside signals that would normally drive profile / federation auto-discovery (``ANTHROPIC_PROFILE``, a ``configs/`` directory, or the workload-identity env trio). Per the credential-precedence spec, the static credential wins and auto-discovery is silently skipped.""" if not _has_auto_discoverable_credentials(): return if api_key is not None and os.environ.get("ANTHROPIC_API_KEY"): warn_env_static_shadows_auto_discovery("ANTHROPIC_API_KEY") if auth_token is not None and os.environ.get("ANTHROPIC_AUTH_TOKEN"): warn_env_static_shadows_auto_discovery("ANTHROPIC_AUTH_TOKEN") # --- end credentials support --- if TYPE_CHECKING: from .resources import beta, models, messages, completions from .resources.models import Models, AsyncModels from .resources.beta.beta import Beta, AsyncBeta from .resources.completions import Completions, AsyncCompletions from .resources.messages.messages import Messages, AsyncMessages __all__ = [ "Timeout", "Transport", "ProxiesTypes", "RequestOptions", "Anthropic", "AsyncAnthropic", "Client", "AsyncClient", ] class Anthropic(SyncAPIClient): # client options api_key: str | None auth_token: str | None webhook_key: str | None credentials: AccessTokenProvider | None _token_cache: TokenCache | None _custom_auth: AccessTokenAuth | None # constants HUMAN_PROMPT = _constants.HUMAN_PROMPT AI_PROMPT = _constants.AI_PROMPT def __init__( self, *, api_key: str | None = None, auth_token: str | None = None, credentials: AccessTokenProvider | None = None, config: Mapping[str, Any] | None = None, profile: str | None = None, webhook_key: str | None = None, base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = not_given, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, # Configure a custom httpx client. # We provide a `DefaultHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`. # See the [httpx documentation](https://www.python-httpx.org/api/#client) for more details. http_client: httpx.Client | None = None, middleware: Sequence[MiddlewareInput] | None = None, # Enable or disable schema validation for data returned by the API. # When enabled an error APIResponseValidationError is raised # if the API responds with invalid data for the expected schema. # # This parameter may be removed or changed in the future. # If you rely on this feature, please open a GitHub issue # outlining your use-case to help us decide if it should be # part of our public interface in the future. _strict_response_validation: bool = False, _token_cache: TokenCache | None | NotGiven = not_given, ) -> None: """Construct a new synchronous Anthropic client instance. Credentials are resolved in the following order (first match wins): 1. Explicit constructor arguments — ``api_key=``, ``auth_token=``, ``credentials=``, ``config=``, or ``profile=``. When any of these is passed, environment variables are not consulted for credentials. 2. ``ANTHROPIC_API_KEY`` / ``ANTHROPIC_AUTH_TOKEN`` environment variables. 3. ``ANTHROPIC_PROFILE`` environment variable — loads the named profile from ``/configs/.json``. 4. Workload identity federation environment variables — ``ANTHROPIC_IDENTITY_TOKEN[_FILE]`` + ``ANTHROPIC_FEDERATION_RULE_ID`` + ``ANTHROPIC_ORGANIZATION_ID``. 5. The active profile on disk — the profile named by ``/active_config``, or ``default``. ``credentials=``, ``config=``, and ``profile=`` are mutually exclusive. If a static credential is supplied alongside a credentials provider (``credentials=`` / ``config=`` / ``profile=``), or if ``ANTHROPIC_API_KEY`` / ``ANTHROPIC_AUTH_TOKEN`` is set alongside a profile or federation configuration, the static credential takes precedence and a one-shot warning is logged on the ``anthropic`` logger. """ # --- credentials support (hand-written, upstream to Stainless) --- # Explicit ctor args are total. If the caller passed any explicit # credential argument, do NOT read credential env vars. has_explicit_credential = ( api_key is not None or auth_token is not None or credentials is not None or config is not None or profile is not None ) if not has_explicit_credential: api_key = os.environ.get("ANTHROPIC_API_KEY") auth_token = os.environ.get("ANTHROPIC_AUTH_TOKEN") self.api_key = api_key self.auth_token = auth_token # --- end credentials support --- if webhook_key is None: webhook_key = os.environ.get("ANTHROPIC_WEBHOOK_SIGNING_KEY") self.webhook_key = webhook_key if base_url is None: base_url = os.environ.get("ANTHROPIC_BASE_URL") # base_url precedence: kwarg > ANTHROPIC_BASE_URL > profile config # (filled in below from default_credentials) > hardcoded default. # Track whether the user supplied one so the profile only fills the # gap, never overrides. base_url_is_explicit = base_url is not None if base_url is None: base_url = f"https://api.anthropic.com" custom_headers_env = os.environ.get("ANTHROPIC_CUSTOM_HEADERS") if custom_headers_env is not None: parsed: dict[str, str] = {} for line in custom_headers_env.split("\n"): colon = line.find(":") if colon >= 0: parsed[line[:colon].strip()] = line[colon + 1 :].strip() default_headers = {**parsed, **(default_headers if is_mapping_t(default_headers) else {})} # --- credentials support (hand-written, upstream to Stainless) --- credential_headers: dict[str, str] = {} if config is not None: if credentials is not None or profile is not None: raise TypeError("Pass at most one of `credentials=`, `config=`, or `profile=`.") in_memory = InMemoryConfig(dict(config)) credentials = in_memory credential_headers = in_memory.extra_headers() if not base_url_is_explicit and in_memory.resolved_base_url: base_url = in_memory.resolved_base_url elif profile is not None: if credentials is not None: raise TypeError("Pass at most one of `credentials=`, `config=`, or `profile=`.") creds_file = CredentialsFile(profile=profile) credentials = creds_file credential_headers = creds_file.extra_headers() if not base_url_is_explicit and creds_file.resolved_base_url: base_url = creds_file.resolved_base_url if credentials is None and api_key is None and auth_token is None and _is_base_client(self): result = default_credentials(base_url=str(base_url) if base_url else "https://api.anthropic.com") if result is not None: credentials = result.provider credential_headers = result.extra_headers if not base_url_is_explicit and result.base_url: base_url = result.base_url _bind_credentials_base_url(credentials, str(base_url)) self.credentials = credentials _warn_explicit_shadow(api_key=api_key, auth_token=auth_token, credentials=credentials) if _is_base_client(self): # Subclasses never run the auto-discovery chain (gated on `_is_base_client` # below), so nothing is shadowed and the warning would be spurious. _warn_env_shadow(api_key=api_key, auth_token=auth_token) if not isinstance(_token_cache, NotGiven): self._token_cache = _token_cache else: self._token_cache = TokenCache(credentials) if credentials is not None else None self._custom_auth = AccessTokenAuth(self._token_cache) if self._token_cache is not None else None if credential_headers: default_headers = {**credential_headers, **(default_headers or {})} # --- end credentials support --- super().__init__( version=__version__, base_url=base_url, max_retries=max_retries, timeout=timeout, http_client=http_client, custom_headers=default_headers, custom_query=default_query, middleware=middleware, _strict_response_validation=_strict_response_validation, ) self._default_stream_cls = Stream @cached_property def completions(self) -> Completions: from .resources.completions import Completions return Completions(self) @cached_property def messages(self) -> Messages: from .resources.messages import Messages return Messages(self) @cached_property def models(self) -> Models: from .resources.models import Models return Models(self) @cached_property def beta(self) -> Beta: from .resources.beta import Beta return Beta(self) @cached_property def with_raw_response(self) -> AnthropicWithRawResponse: return AnthropicWithRawResponse(self) @cached_property def with_streaming_response(self) -> AnthropicWithStreamedResponse: return AnthropicWithStreamedResponse(self) @property @override def qs(self) -> Querystring: return Querystring(array_format="brackets") @property @override def auth_headers(self) -> dict[str, str]: return {**self._api_key_auth, **self._bearer_auth} @property def _api_key_auth(self) -> dict[str, str]: api_key = self.api_key if api_key is None: return {} return {"X-Api-Key": api_key} @property def _bearer_auth(self) -> dict[str, str]: # Symmetric with _api_key_auth: always emit if self.auth_token is set, # regardless of whether a TokenCache is also installed. When both a # static auth_token and a credentials provider are present, the static # credential wins per the documented precedence — AccessTokenAuth # short-circuits on a pre-set Authorization header and no token # exchange runs. auth_token = self.auth_token if auth_token is None: return {} return {"Authorization": f"Bearer {auth_token}"} @property @override def default_headers(self) -> dict[str, str | Omit]: return { **super().default_headers, "X-Stainless-Async": "false", "anthropic-version": "2023-06-01", **self._custom_headers, } @override def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: # --- credentials support (hand-written, upstream to Stainless) --- # The token cache *may* inject an Authorization header per-request via # custom_auth, so validation that checks only default_headers would # false-negative when credentials are the only auth source. Defer to # the static-header check below — if a static api_key or auth_token # is set it will already be on default_headers; otherwise custom_auth # will fill in Authorization at request time. if self._token_cache is not None and not headers.get("X-Api-Key") and not headers.get("Authorization"): return # --- end credentials support --- if headers.get("Authorization") or headers.get("X-Api-Key"): # valid return if headers.get("X-Api-Key") or isinstance(custom_headers.get("X-Api-Key"), Omit): return if headers.get("Authorization") or isinstance(custom_headers.get("Authorization"), Omit): return raise TypeError( '"Could not resolve authentication method. Expected one of api_key, auth_token, or credentials to be set. Or for one of the `X-Api-Key` or `Authorization` headers to be explicitly omitted"' ) # --- credentials support (hand-written, upstream to Stainless) --- @property @override def custom_auth(self) -> httpx.Auth | None: return self._custom_auth @override def _should_retry(self, response: httpx.Response) -> bool: # On 401 with a token cache, invalidate and retry once so the request # is re-sent with a freshly minted Bearer token. The base-client retry # loop rebuilds the request from FinalRequestOptions on each attempt, # so body replay is handled for us. The single-shot guard relies on # ``x-stainless-retry-count`` being ``"0"`` on the first attempt # (see _base_client.py); if a caller Omit()s that header the guard # silently no-ops, which fails safe (no retry, surface the 401). if response.status_code == 401 and self._token_cache is not None: self._token_cache.invalidate() if response.request.headers.get("x-stainless-retry-count") == "0": return True return super()._should_retry(response) @override def close(self) -> None: super().close() _close_credentials(self.credentials) # --- end credentials support --- def copy( self, *, api_key: str | None = None, auth_token: str | None = None, credentials: AccessTokenProvider | None | NotGiven = not_given, config: Mapping[str, Any] | None = None, profile: str | None = None, webhook_key: str | None = None, base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.Client | None = None, max_retries: int | NotGiven = not_given, default_headers: Mapping[str, str] | None = None, set_default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, set_default_query: Mapping[str, object] | None = None, middleware: Sequence[MiddlewareInput] | None | NotGiven = not_given, _extra_kwargs: Mapping[str, Any] = {}, ) -> Self: """ Create a new client instance re-using the same options given to the current client with optional overriding. """ if default_headers is not None and set_default_headers is not None: raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") if default_query is not None and set_default_query is not None: raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") headers = self._custom_headers if default_headers is not None: headers = merge_headers(headers, default_headers) elif set_default_headers is not None: headers = set_default_headers params = self._custom_query if default_query is not None: params = {**params, **default_query} elif set_default_query is not None: params = set_default_query http_client = http_client or self._client # --- credentials support (hand-written, upstream to Stainless) --- if config is not None: if not isinstance(credentials, NotGiven) or profile is not None: raise TypeError("Pass at most one of `credentials=`, `config=`, or `profile=`.") _extra_kwargs = {"config": config, **_extra_kwargs} elif profile is not None: if not isinstance(credentials, NotGiven): raise TypeError("Pass at most one of `credentials=`, `config=`, or `profile=`.") _extra_kwargs = {"profile": profile, **_extra_kwargs} else: resolved_credentials = self.credentials if isinstance(credentials, NotGiven) else credentials if resolved_credentials is not None and _is_base_client(self): _extra_kwargs = {"credentials": resolved_credentials, **_extra_kwargs} # Reuse the parent's TokenCache when the credentials provider is # unchanged so with_options() copies don't trigger an independent # token exchange. A new credentials= gets a fresh cache. if isinstance(credentials, NotGiven): _extra_kwargs = {"_token_cache": self._token_cache, **_extra_kwargs} # --- end credentials support --- return self.__class__( api_key=api_key or self.api_key, auth_token=auth_token or self.auth_token, webhook_key=webhook_key or self.webhook_key, base_url=base_url or self.base_url, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client, max_retries=max_retries if is_given(max_retries) else self.max_retries, default_headers=headers, default_query=params, middleware=self._middleware if isinstance(middleware, NotGiven) else middleware, **_extra_kwargs, ) # Alias for `copy` for nicer inline usage, e.g. # client.with_options(timeout=10).foo.create(...) with_options = copy def with_middleware(self, *middleware: MiddlewareInput) -> Self: """A new client with the given middleware appended after this client's middleware. Convenience for applying extra middleware to a single request: ```py client.with_middleware(my_middleware).messages.create(...) ``` """ return self.copy(middleware=[*self._middleware, *middleware]) @override def _make_status_error( self, err_msg: str, *, body: object, response: httpx.Response, ) -> APIStatusError: if response.status_code == 400: return _exceptions.BadRequestError(err_msg, response=response, body=body) if response.status_code == 401: return _exceptions.AuthenticationError(err_msg, response=response, body=body) if response.status_code == 403: return _exceptions.PermissionDeniedError(err_msg, response=response, body=body) if response.status_code == 404: return _exceptions.NotFoundError(err_msg, response=response, body=body) if response.status_code == 409: return _exceptions.ConflictError(err_msg, response=response, body=body) if response.status_code == 413: return _exceptions.RequestTooLargeError(err_msg, response=response, body=body) if response.status_code == 422: return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body) if response.status_code == 429: return _exceptions.RateLimitError(err_msg, response=response, body=body) if response.status_code == 529: return _exceptions.OverloadedError(err_msg, response=response, body=body) if response.status_code >= 500: return _exceptions.InternalServerError(err_msg, response=response, body=body) return APIStatusError(err_msg, response=response, body=body) class AsyncAnthropic(AsyncAPIClient): # client options api_key: str | None auth_token: str | None webhook_key: str | None credentials: AccessTokenProvider | None _token_cache: TokenCache | None _custom_auth: AccessTokenAuth | None # constants HUMAN_PROMPT = _constants.HUMAN_PROMPT AI_PROMPT = _constants.AI_PROMPT def __init__( self, *, api_key: str | None = None, auth_token: str | None = None, credentials: AccessTokenProvider | None = None, config: Mapping[str, Any] | None = None, profile: str | None = None, webhook_key: str | None = None, base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = not_given, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, # Configure a custom httpx client. # We provide a `DefaultAsyncHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`. # See the [httpx documentation](https://www.python-httpx.org/api/#asyncclient) for more details. http_client: httpx.AsyncClient | None = None, middleware: Sequence[MiddlewareInput] | None = None, # Enable or disable schema validation for data returned by the API. # When enabled an error APIResponseValidationError is raised # if the API responds with invalid data for the expected schema. # # This parameter may be removed or changed in the future. # If you rely on this feature, please open a GitHub issue # outlining your use-case to help us decide if it should be # part of our public interface in the future. _strict_response_validation: bool = False, _token_cache: TokenCache | None | NotGiven = not_given, ) -> None: """Construct a new async AsyncAnthropic client instance. Credentials are resolved in the following order (first match wins): 1. Explicit constructor arguments — ``api_key=``, ``auth_token=``, ``credentials=``, ``config=``, or ``profile=``. When any of these is passed, environment variables are not consulted for credentials. 2. ``ANTHROPIC_API_KEY`` / ``ANTHROPIC_AUTH_TOKEN`` environment variables. 3. ``ANTHROPIC_PROFILE`` environment variable — loads the named profile from ``/configs/.json``. 4. Workload identity federation environment variables — ``ANTHROPIC_IDENTITY_TOKEN[_FILE]`` + ``ANTHROPIC_FEDERATION_RULE_ID`` + ``ANTHROPIC_ORGANIZATION_ID``. 5. The active profile on disk — the profile named by ``/active_config``, or ``default``. ``credentials=``, ``config=``, and ``profile=`` are mutually exclusive. If a static credential is supplied alongside a credentials provider (``credentials=`` / ``config=`` / ``profile=``), or if ``ANTHROPIC_API_KEY`` / ``ANTHROPIC_AUTH_TOKEN`` is set alongside a profile or federation configuration, the static credential takes precedence and a one-shot warning is logged on the ``anthropic`` logger. """ # --- credentials support (hand-written, upstream to Stainless) --- # Explicit ctor args are total. If the caller passed any explicit # credential argument, do NOT read credential env vars. has_explicit_credential = ( api_key is not None or auth_token is not None or credentials is not None or config is not None or profile is not None ) if not has_explicit_credential: api_key = os.environ.get("ANTHROPIC_API_KEY") auth_token = os.environ.get("ANTHROPIC_AUTH_TOKEN") self.api_key = api_key self.auth_token = auth_token # --- end credentials support --- if webhook_key is None: webhook_key = os.environ.get("ANTHROPIC_WEBHOOK_SIGNING_KEY") self.webhook_key = webhook_key if base_url is None: base_url = os.environ.get("ANTHROPIC_BASE_URL") # base_url precedence: kwarg > ANTHROPIC_BASE_URL > profile config # (filled in below from default_credentials) > hardcoded default. # Track whether the user supplied one so the profile only fills the # gap, never overrides. base_url_is_explicit = base_url is not None if base_url is None: base_url = f"https://api.anthropic.com" custom_headers_env = os.environ.get("ANTHROPIC_CUSTOM_HEADERS") if custom_headers_env is not None: parsed: dict[str, str] = {} for line in custom_headers_env.split("\n"): colon = line.find(":") if colon >= 0: parsed[line[:colon].strip()] = line[colon + 1 :].strip() default_headers = {**parsed, **(default_headers if is_mapping_t(default_headers) else {})} # --- credentials support (hand-written, upstream to Stainless) --- credential_headers: dict[str, str] = {} if config is not None: if credentials is not None or profile is not None: raise TypeError("Pass at most one of `credentials=`, `config=`, or `profile=`.") in_memory = InMemoryConfig(dict(config)) credentials = in_memory credential_headers = in_memory.extra_headers() if not base_url_is_explicit and in_memory.resolved_base_url: base_url = in_memory.resolved_base_url elif profile is not None: if credentials is not None: raise TypeError("Pass at most one of `credentials=`, `config=`, or `profile=`.") creds_file = CredentialsFile(profile=profile) credentials = creds_file credential_headers = creds_file.extra_headers() if not base_url_is_explicit and creds_file.resolved_base_url: base_url = creds_file.resolved_base_url if credentials is None and api_key is None and auth_token is None and _is_base_client(self): result = default_credentials(base_url=str(base_url) if base_url else "https://api.anthropic.com") if result is not None: credentials = result.provider credential_headers = result.extra_headers if not base_url_is_explicit and result.base_url: base_url = result.base_url _bind_credentials_base_url(credentials, str(base_url)) self.credentials = credentials _warn_explicit_shadow(api_key=api_key, auth_token=auth_token, credentials=credentials) if _is_base_client(self): # Subclasses never run the auto-discovery chain (gated on `_is_base_client` # below), so nothing is shadowed and the warning would be spurious. _warn_env_shadow(api_key=api_key, auth_token=auth_token) if not isinstance(_token_cache, NotGiven): self._token_cache = _token_cache else: self._token_cache = TokenCache(credentials) if credentials is not None else None self._custom_auth = AccessTokenAuth(self._token_cache) if self._token_cache is not None else None if credential_headers: default_headers = {**credential_headers, **(default_headers or {})} # --- end credentials support --- super().__init__( version=__version__, base_url=base_url, max_retries=max_retries, timeout=timeout, http_client=http_client, custom_headers=default_headers, custom_query=default_query, middleware=middleware, _strict_response_validation=_strict_response_validation, ) self._default_stream_cls = AsyncStream @cached_property def completions(self) -> AsyncCompletions: from .resources.completions import AsyncCompletions return AsyncCompletions(self) @cached_property def messages(self) -> AsyncMessages: from .resources.messages import AsyncMessages return AsyncMessages(self) @cached_property def models(self) -> AsyncModels: from .resources.models import AsyncModels return AsyncModels(self) @cached_property def beta(self) -> AsyncBeta: from .resources.beta import AsyncBeta return AsyncBeta(self) @cached_property def with_raw_response(self) -> AsyncAnthropicWithRawResponse: return AsyncAnthropicWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncAnthropicWithStreamedResponse: return AsyncAnthropicWithStreamedResponse(self) @property @override def qs(self) -> Querystring: return Querystring(array_format="brackets") @property @override def auth_headers(self) -> dict[str, str]: return {**self._api_key_auth, **self._bearer_auth} @property def _api_key_auth(self) -> dict[str, str]: api_key = self.api_key if api_key is None: return {} return {"X-Api-Key": api_key} @property def _bearer_auth(self) -> dict[str, str]: # Symmetric with _api_key_auth: always emit if self.auth_token is set, # regardless of whether a TokenCache is also installed. When both a # static auth_token and a credentials provider are present, the static # credential wins per the documented precedence — AccessTokenAuth # short-circuits on a pre-set Authorization header and no token # exchange runs. auth_token = self.auth_token if auth_token is None: return {} return {"Authorization": f"Bearer {auth_token}"} @property @override def default_headers(self) -> dict[str, str | Omit]: return { **super().default_headers, "X-Stainless-Async": f"async:{get_async_library()}", "anthropic-version": "2023-06-01", **self._custom_headers, } @override def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: # --- credentials support (hand-written, upstream to Stainless) --- if self._token_cache is not None and not headers.get("X-Api-Key") and not headers.get("Authorization"): return # --- end credentials support --- if headers.get("Authorization") or headers.get("X-Api-Key"): # valid return if headers.get("X-Api-Key") or isinstance(custom_headers.get("X-Api-Key"), Omit): return if headers.get("Authorization") or isinstance(custom_headers.get("Authorization"), Omit): return raise TypeError( '"Could not resolve authentication method. Expected one of api_key, auth_token, or credentials to be set. Or for one of the `X-Api-Key` or `Authorization` headers to be explicitly omitted"' ) # --- credentials support (hand-written, upstream to Stainless) --- @property @override def custom_auth(self) -> httpx.Auth | None: return self._custom_auth @override def _should_retry(self, response: httpx.Response) -> bool: # On 401 with a token cache, invalidate and retry once so the request # is re-sent with a freshly minted Bearer token. The base-client retry # loop rebuilds the request from FinalRequestOptions on each attempt, # so body replay is handled for us. The single-shot guard relies on # ``x-stainless-retry-count`` being ``"0"`` on the first attempt # (see _base_client.py); if a caller Omit()s that header the guard # silently no-ops, which fails safe (no retry, surface the 401). if response.status_code == 401 and self._token_cache is not None: self._token_cache.invalidate() if response.request.headers.get("x-stainless-retry-count") == "0": return True return super()._should_retry(response) @override async def close(self) -> None: await super().close() # Credential providers expose a sync close() even from the async client — # they own a sync httpx.Client for the token-exchange POST. _close_credentials(self.credentials) # --- end credentials support --- def copy( self, *, api_key: str | None = None, auth_token: str | None = None, credentials: AccessTokenProvider | None | NotGiven = not_given, config: Mapping[str, Any] | None = None, profile: str | None = None, webhook_key: str | None = None, base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = not_given, http_client: httpx.AsyncClient | None = None, max_retries: int | NotGiven = not_given, default_headers: Mapping[str, str] | None = None, set_default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, set_default_query: Mapping[str, object] | None = None, middleware: Sequence[MiddlewareInput] | None | NotGiven = not_given, _extra_kwargs: Mapping[str, Any] = {}, ) -> Self: """ Create a new client instance re-using the same options given to the current client with optional overriding. """ if default_headers is not None and set_default_headers is not None: raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") if default_query is not None and set_default_query is not None: raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") headers = self._custom_headers if default_headers is not None: headers = merge_headers(headers, default_headers) elif set_default_headers is not None: headers = set_default_headers params = self._custom_query if default_query is not None: params = {**params, **default_query} elif set_default_query is not None: params = set_default_query http_client = http_client or self._client # --- credentials support (hand-written, upstream to Stainless) --- if config is not None: if not isinstance(credentials, NotGiven) or profile is not None: raise TypeError("Pass at most one of `credentials=`, `config=`, or `profile=`.") _extra_kwargs = {"config": config, **_extra_kwargs} elif profile is not None: if not isinstance(credentials, NotGiven): raise TypeError("Pass at most one of `credentials=`, `config=`, or `profile=`.") _extra_kwargs = {"profile": profile, **_extra_kwargs} else: resolved_credentials = self.credentials if isinstance(credentials, NotGiven) else credentials if resolved_credentials is not None and _is_base_client(self): _extra_kwargs = {"credentials": resolved_credentials, **_extra_kwargs} # Reuse the parent's TokenCache when the credentials provider is # unchanged so with_options() copies don't trigger an independent # token exchange. A new credentials= gets a fresh cache. if isinstance(credentials, NotGiven): _extra_kwargs = {"_token_cache": self._token_cache, **_extra_kwargs} # --- end credentials support --- return self.__class__( api_key=api_key or self.api_key, auth_token=auth_token or self.auth_token, webhook_key=webhook_key or self.webhook_key, base_url=base_url or self.base_url, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client, max_retries=max_retries if is_given(max_retries) else self.max_retries, default_headers=headers, default_query=params, middleware=self._middleware if isinstance(middleware, NotGiven) else middleware, **_extra_kwargs, ) # Alias for `copy` for nicer inline usage, e.g. # client.with_options(timeout=10).foo.create(...) with_options = copy def with_middleware(self, *middleware: MiddlewareInput) -> Self: """A new client with the given middleware appended after this client's middleware. Convenience for applying extra middleware to a single request: ```py client.with_middleware(my_middleware).messages.create(...) ``` """ return self.copy(middleware=[*self._middleware, *middleware]) @override def _make_status_error( self, err_msg: str, *, body: object, response: httpx.Response, ) -> APIStatusError: if response.status_code == 400: return _exceptions.BadRequestError(err_msg, response=response, body=body) if response.status_code == 401: return _exceptions.AuthenticationError(err_msg, response=response, body=body) if response.status_code == 403: return _exceptions.PermissionDeniedError(err_msg, response=response, body=body) if response.status_code == 404: return _exceptions.NotFoundError(err_msg, response=response, body=body) if response.status_code == 409: return _exceptions.ConflictError(err_msg, response=response, body=body) if response.status_code == 413: return _exceptions.RequestTooLargeError(err_msg, response=response, body=body) if response.status_code == 422: return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body) if response.status_code == 429: return _exceptions.RateLimitError(err_msg, response=response, body=body) if response.status_code == 529: return _exceptions.OverloadedError(err_msg, response=response, body=body) if response.status_code >= 500: return _exceptions.InternalServerError(err_msg, response=response, body=body) return APIStatusError(err_msg, response=response, body=body) class AnthropicWithRawResponse: _client: Anthropic def __init__(self, client: Anthropic) -> None: self._client = client @cached_property def completions(self) -> completions.CompletionsWithRawResponse: from .resources.completions import CompletionsWithRawResponse return CompletionsWithRawResponse(self._client.completions) @cached_property def messages(self) -> messages.MessagesWithRawResponse: from .resources.messages import MessagesWithRawResponse return MessagesWithRawResponse(self._client.messages) @cached_property def models(self) -> models.ModelsWithRawResponse: from .resources.models import ModelsWithRawResponse return ModelsWithRawResponse(self._client.models) @cached_property def beta(self) -> beta.BetaWithRawResponse: from .resources.beta import BetaWithRawResponse return BetaWithRawResponse(self._client.beta) class AsyncAnthropicWithRawResponse: _client: AsyncAnthropic def __init__(self, client: AsyncAnthropic) -> None: self._client = client @cached_property def completions(self) -> completions.AsyncCompletionsWithRawResponse: from .resources.completions import AsyncCompletionsWithRawResponse return AsyncCompletionsWithRawResponse(self._client.completions) @cached_property def messages(self) -> messages.AsyncMessagesWithRawResponse: from .resources.messages import AsyncMessagesWithRawResponse return AsyncMessagesWithRawResponse(self._client.messages) @cached_property def models(self) -> models.AsyncModelsWithRawResponse: from .resources.models import AsyncModelsWithRawResponse return AsyncModelsWithRawResponse(self._client.models) @cached_property def beta(self) -> beta.AsyncBetaWithRawResponse: from .resources.beta import AsyncBetaWithRawResponse return AsyncBetaWithRawResponse(self._client.beta) class AnthropicWithStreamedResponse: _client: Anthropic def __init__(self, client: Anthropic) -> None: self._client = client @cached_property def completions(self) -> completions.CompletionsWithStreamingResponse: from .resources.completions import CompletionsWithStreamingResponse return CompletionsWithStreamingResponse(self._client.completions) @cached_property def messages(self) -> messages.MessagesWithStreamingResponse: from .resources.messages import MessagesWithStreamingResponse return MessagesWithStreamingResponse(self._client.messages) @cached_property def models(self) -> models.ModelsWithStreamingResponse: from .resources.models import ModelsWithStreamingResponse return ModelsWithStreamingResponse(self._client.models) @cached_property def beta(self) -> beta.BetaWithStreamingResponse: from .resources.beta import BetaWithStreamingResponse return BetaWithStreamingResponse(self._client.beta) class AsyncAnthropicWithStreamedResponse: _client: AsyncAnthropic def __init__(self, client: AsyncAnthropic) -> None: self._client = client @cached_property def completions(self) -> completions.AsyncCompletionsWithStreamingResponse: from .resources.completions import AsyncCompletionsWithStreamingResponse return AsyncCompletionsWithStreamingResponse(self._client.completions) @cached_property def messages(self) -> messages.AsyncMessagesWithStreamingResponse: from .resources.messages import AsyncMessagesWithStreamingResponse return AsyncMessagesWithStreamingResponse(self._client.messages) @cached_property def models(self) -> models.AsyncModelsWithStreamingResponse: from .resources.models import AsyncModelsWithStreamingResponse return AsyncModelsWithStreamingResponse(self._client.models) @cached_property def beta(self) -> beta.AsyncBetaWithStreamingResponse: from .resources.beta import AsyncBetaWithStreamingResponse return AsyncBetaWithStreamingResponse(self._client.beta) Client = Anthropic AsyncClient = AsyncAnthropic anthropic-sdk-python-0.120.2/src/anthropic/_compat.py000066400000000000000000000155601523216435200225250ustar00rootroot00000000000000from __future__ import annotations from typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, cast, overload from datetime import date, datetime from typing_extensions import Self, Literal, TypedDict import pydantic from pydantic.fields import FieldInfo from ._types import IncEx, StrBytesIntFloat _T = TypeVar("_T") _ModelT = TypeVar("_ModelT", bound=pydantic.BaseModel) # --------------- Pydantic v2, v3 compatibility --------------- # Pyright incorrectly reports some of our functions as overriding a method when they don't # pyright: reportIncompatibleMethodOverride=false PYDANTIC_V1 = pydantic.VERSION.startswith("1.") if TYPE_CHECKING: def parse_date(value: date | StrBytesIntFloat) -> date: # noqa: ARG001 ... def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: # noqa: ARG001 ... def get_args(t: type[Any]) -> tuple[Any, ...]: # noqa: ARG001 ... def is_union(tp: type[Any] | None) -> bool: # noqa: ARG001 ... def get_origin(t: type[Any]) -> type[Any] | None: # noqa: ARG001 ... def is_literal_type(type_: type[Any]) -> bool: # noqa: ARG001 ... def is_typeddict(type_: type[Any]) -> bool: # noqa: ARG001 ... else: # v1 re-exports if PYDANTIC_V1: from pydantic.typing import ( get_args as get_args, is_union as is_union, get_origin as get_origin, is_typeddict as is_typeddict, is_literal_type as is_literal_type, ) from pydantic.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime else: from ._utils import ( get_args as get_args, is_union as is_union, get_origin as get_origin, parse_date as parse_date, is_typeddict as is_typeddict, parse_datetime as parse_datetime, is_literal_type as is_literal_type, ) # refactored config if TYPE_CHECKING: from pydantic import ConfigDict as ConfigDict else: if PYDANTIC_V1: # TODO: provide an error message here? ConfigDict = None else: from pydantic import ConfigDict as ConfigDict # renamed methods / properties def parse_obj(model: type[_ModelT], value: object) -> _ModelT: if PYDANTIC_V1: return cast(_ModelT, model.parse_obj(value)) # pyright: ignore[reportDeprecated, reportUnnecessaryCast] else: return model.model_validate(value) def field_is_required(field: FieldInfo) -> bool: if PYDANTIC_V1: return field.required # type: ignore return field.is_required() def field_get_default(field: FieldInfo) -> Any: value = field.get_default() if PYDANTIC_V1: return value from pydantic_core import PydanticUndefined if value == PydanticUndefined: return None return value def field_outer_type(field: FieldInfo) -> Any: if PYDANTIC_V1: return field.outer_type_ # type: ignore return field.annotation def get_model_config(model: type[pydantic.BaseModel]) -> Any: if PYDANTIC_V1: return model.__config__ # type: ignore return model.model_config def get_model_fields(model: type[pydantic.BaseModel]) -> dict[str, FieldInfo]: if PYDANTIC_V1: return model.__fields__ # type: ignore return model.model_fields def model_copy(model: _ModelT, *, deep: bool = False) -> _ModelT: if PYDANTIC_V1: return model.copy(deep=deep) # type: ignore return model.model_copy(deep=deep) def model_json(model: pydantic.BaseModel, *, indent: int | None = None) -> str: if PYDANTIC_V1: return model.json(indent=indent) # type: ignore return model.model_dump_json(indent=indent) def model_parse_json(model: type[_ModelT], data: str | bytes) -> _ModelT: if PYDANTIC_V1: return model.parse_raw(data) # pyright: ignore[reportDeprecated] return model.model_validate_json(data) class _ModelDumpKwargs(TypedDict, total=False): by_alias: bool def model_dump( model: pydantic.BaseModel, *, exclude: IncEx | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, warnings: bool = True, mode: Literal["json", "python"] = "python", by_alias: bool | None = None, ) -> dict[str, Any]: if (not PYDANTIC_V1) or hasattr(model, "model_dump"): kwargs: _ModelDumpKwargs = {} if by_alias is not None: kwargs["by_alias"] = by_alias return model.model_dump( mode=mode, exclude=exclude, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, # warnings are not supported in Pydantic v1 warnings=True if PYDANTIC_V1 else warnings, **kwargs, ) return cast( "dict[str, Any]", model.dict( # pyright: ignore[reportDeprecated, reportUnnecessaryCast] exclude=exclude, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, by_alias=bool(by_alias) ), ) def model_parse(model: type[_ModelT], data: Any) -> _ModelT: if PYDANTIC_V1: return model.parse_obj(data) # pyright: ignore[reportDeprecated] return model.model_validate(data) # generic models if TYPE_CHECKING: class GenericModel(pydantic.BaseModel): ... else: if PYDANTIC_V1: import pydantic.generics class GenericModel(pydantic.generics.GenericModel, pydantic.BaseModel): ... else: # there no longer needs to be a distinction in v2 but # we still have to create our own subclass to avoid # inconsistent MRO ordering errors class GenericModel(pydantic.BaseModel): ... # cached properties if TYPE_CHECKING: cached_property = property # we define a separate type (copied from typeshed) # that represents that `cached_property` is `set`able # at runtime, which differs from `@property`. # # this is a separate type as editors likely special case # `@property` and we don't want to cause issues just to have # more helpful internal types. class typed_cached_property(Generic[_T]): func: Callable[[Any], _T] attrname: str | None def __init__(self, func: Callable[[Any], _T]) -> None: ... @overload def __get__(self, instance: None, owner: type[Any] | None = None) -> Self: ... @overload def __get__(self, instance: object, owner: type[Any] | None = None) -> _T: ... def __get__(self, instance: object, owner: type[Any] | None = None) -> _T | Self: raise NotImplementedError() def __set_name__(self, owner: type[Any], name: str) -> None: ... # __set__ is not defined at runtime, but @cached_property is designed to be settable def __set__(self, instance: object, value: _T) -> None: ... else: from functools import cached_property as cached_property typed_cached_property = cached_property anthropic-sdk-python-0.120.2/src/anthropic/_constants.py000066400000000000000000000015651523216435200232560ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. import httpx RAW_RESPONSE_HEADER = "X-Stainless-Raw-Response" OVERRIDE_CAST_TO_HEADER = "____stainless_override_cast_to" # default timeout is 10 minutes DEFAULT_TIMEOUT = httpx.Timeout(timeout=10 * 60, connect=5.0) DEFAULT_MAX_RETRIES = 2 DEFAULT_CONNECTION_LIMITS = httpx.Limits(max_connections=1000, max_keepalive_connections=100) INITIAL_RETRY_DELAY = 0.5 MAX_RETRY_DELAY = 8.0 HUMAN_PROMPT = "\n\nHuman:" AI_PROMPT = "\n\nAssistant:" MODEL_NONSTREAMING_TOKENS = { "claude-opus-4-20250514": 8_192, "claude-opus-4-0": 8_192, "claude-4-opus-20250514": 8_192, "anthropic.claude-opus-4-20250514-v1:0": 8_192, "claude-opus-4@20250514": 8_192, "claude-opus-4-1-20250805": 8192, "anthropic.claude-opus-4-1-20250805-v1:0": 8192, "claude-opus-4-1@20250805": 8192, } anthropic-sdk-python-0.120.2/src/anthropic/_decoders/000077500000000000000000000000001523216435200224515ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/_decoders/jsonl.py000066400000000000000000000066661523216435200241660ustar00rootroot00000000000000from __future__ import annotations import json from typing_extensions import Generic, TypeVar, Iterator, AsyncIterator import httpx from .._models import construct_type_unchecked _T = TypeVar("_T") class JSONLDecoder(Generic[_T]): """A decoder for [JSON Lines](https://jsonlines.org) format. This class provides an iterator over a byte-iterator that parses each JSON Line into a given type. """ http_response: httpx.Response """The HTTP response this decoder was constructed from""" def __init__( self, *, raw_iterator: Iterator[bytes], line_type: type[_T], http_response: httpx.Response, ) -> None: super().__init__() self.http_response = http_response self._raw_iterator = raw_iterator self._line_type = line_type self._iterator = self.__decode__() def close(self) -> None: """Close the response body stream. This is called automatically if you consume the entire stream. """ self.http_response.close() def __decode__(self) -> Iterator[_T]: buf = b"" for chunk in self._raw_iterator: for line in chunk.splitlines(keepends=True): buf += line if buf.endswith((b"\r", b"\n", b"\r\n")): yield construct_type_unchecked( value=json.loads(buf), type_=self._line_type, ) buf = b"" # flush if buf: yield construct_type_unchecked( value=json.loads(buf), type_=self._line_type, ) def __next__(self) -> _T: return self._iterator.__next__() def __iter__(self) -> Iterator[_T]: for item in self._iterator: yield item class AsyncJSONLDecoder(Generic[_T]): """A decoder for [JSON Lines](https://jsonlines.org) format. This class provides an async iterator over a byte-iterator that parses each JSON Line into a given type. """ http_response: httpx.Response def __init__( self, *, raw_iterator: AsyncIterator[bytes], line_type: type[_T], http_response: httpx.Response, ) -> None: super().__init__() self.http_response = http_response self._raw_iterator = raw_iterator self._line_type = line_type self._iterator = self.__decode__() async def close(self) -> None: """Close the response body stream. This is called automatically if you consume the entire stream. """ await self.http_response.aclose() async def __decode__(self) -> AsyncIterator[_T]: buf = b"" async for chunk in self._raw_iterator: for line in chunk.splitlines(keepends=True): buf += line if buf.endswith((b"\r", b"\n", b"\r\n")): yield construct_type_unchecked( value=json.loads(buf), type_=self._line_type, ) buf = b"" # flush if buf: yield construct_type_unchecked( value=json.loads(buf), type_=self._line_type, ) async def __anext__(self) -> _T: return await self._iterator.__anext__() async def __aiter__(self) -> AsyncIterator[_T]: async for item in self._iterator: yield item anthropic-sdk-python-0.120.2/src/anthropic/_exceptions.py000066400000000000000000000112441523216435200234160ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, cast from typing_extensions import Literal import httpx from ._utils import is_dict from .types.shared.error_type import ErrorType __all__ = [ "BadRequestError", "AuthenticationError", "PermissionDeniedError", "NotFoundError", "ConflictError", "UnprocessableEntityError", "RateLimitError", "InternalServerError", ] class AnthropicError(Exception): pass class APIError(AnthropicError): message: str request: httpx.Request body: object | None """The API response body. If the API responded with a valid JSON structure then this property will be the decoded result. If it isn't a valid JSON structure then this will be the raw response. If there was no response associated with this error then it will be `None`. """ def __init__(self, message: str, request: httpx.Request, *, body: object | None) -> None: # noqa: ARG002 super().__init__(message) self.request = request self.message = message self.body = body class APIResponseValidationError(APIError): response: httpx.Response status_code: int def __init__(self, response: httpx.Response, body: object | None, *, message: str | None = None) -> None: super().__init__(message or "Data returned by API invalid for expected schema.", response.request, body=body) self.response = response self.status_code = response.status_code class APIWebhookValidationError(APIError): pass class APIStatusError(APIError): """Raised when an API response has a status code of 4xx or 5xx.""" response: httpx.Response status_code: int request_id: str | None type: ErrorType | None def __init__(self, message: str, *, response: httpx.Response, body: object | None) -> None: super().__init__(message, response.request, body=body) self.response = response self.status_code = response.status_code self.request_id = response.headers.get("request-id") self.type = None if is_dict(body): error = body.get("error") if is_dict(error): self.type = cast(Union[ErrorType, None], error.get("type")) class APIConnectionError(APIError): def __init__(self, *, message: str = "Connection error.", request: httpx.Request) -> None: super().__init__(message, request, body=None) class APITimeoutError(APIConnectionError): def __init__(self, request: httpx.Request) -> None: super().__init__( message="Request timed out or interrupted. This could be due to a network timeout, dropped connection, or request cancellation. See https://docs.anthropic.com/en/api/errors#long-requests for more details.", request=request, ) class RetryableError(AnthropicError): """An error that opts into the SDK's retry policy: raise it (e.g. from middleware) to have the request attempt retried. The request is only retried while `max_retries` has not been exhausted; once exhausted the error propagates to the caller as-is. """ class BadRequestError(APIStatusError): status_code: Literal[400] = 400 # pyright: ignore[reportIncompatibleVariableOverride] class AuthenticationError(APIStatusError): status_code: Literal[401] = 401 # pyright: ignore[reportIncompatibleVariableOverride] class PermissionDeniedError(APIStatusError): status_code: Literal[403] = 403 # pyright: ignore[reportIncompatibleVariableOverride] class NotFoundError(APIStatusError): status_code: Literal[404] = 404 # pyright: ignore[reportIncompatibleVariableOverride] class ConflictError(APIStatusError): status_code: Literal[409] = 409 # pyright: ignore[reportIncompatibleVariableOverride] class RequestTooLargeError(APIStatusError): status_code: Literal[413] = 413 # pyright: ignore[reportIncompatibleVariableOverride] class UnprocessableEntityError(APIStatusError): status_code: Literal[422] = 422 # pyright: ignore[reportIncompatibleVariableOverride] class RateLimitError(APIStatusError): status_code: Literal[429] = 429 # pyright: ignore[reportIncompatibleVariableOverride] class ServiceUnavailableError(APIStatusError): status_code: Literal[503] = 503 # pyright: ignore[reportIncompatibleVariableOverride] class OverloadedError(APIStatusError): status_code: Literal[529] = 529 # pyright: ignore[reportIncompatibleVariableOverride] class DeadlineExceededError(APIStatusError): status_code: Literal[504] = 504 # pyright: ignore[reportIncompatibleVariableOverride] class InternalServerError(APIStatusError): pass anthropic-sdk-python-0.120.2/src/anthropic/_files.py000066400000000000000000000126331523216435200223420ustar00rootroot00000000000000from __future__ import annotations import io import os import pathlib from typing import Sequence, cast, overload from typing_extensions import TypeVar, TypeGuard import anyio from ._types import ( FileTypes, FileContent, RequestFiles, HttpxFileTypes, Base64FileInput, HttpxFileContent, HttpxRequestFiles, ) from ._utils import is_list, is_mapping, is_tuple_t, is_mapping_t, is_sequence_t _T = TypeVar("_T") def is_base64_file_input(obj: object) -> TypeGuard[Base64FileInput]: return isinstance(obj, io.IOBase) or isinstance(obj, os.PathLike) def is_file_content(obj: object) -> TypeGuard[FileContent]: return ( isinstance(obj, bytes) or isinstance(obj, tuple) or isinstance(obj, io.IOBase) or isinstance(obj, os.PathLike) ) def assert_is_file_content(obj: object, *, key: str | None = None) -> None: if not is_file_content(obj): prefix = f"Expected entry at `{key}`" if key is not None else f"Expected file input `{obj!r}`" raise RuntimeError( f"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(obj)} instead. See https://github.com/anthropics/anthropic-sdk-python/tree/main#file-uploads" ) from None @overload def to_httpx_files(files: None) -> None: ... @overload def to_httpx_files(files: RequestFiles) -> HttpxRequestFiles: ... def to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles | None: if files is None: return None if is_mapping_t(files): files = {key: _transform_file(file) for key, file in files.items()} elif is_sequence_t(files): files = [(key, _transform_file(file)) for key, file in files] else: raise TypeError(f"Unexpected file type input {type(files)}, expected mapping or sequence") return files def _transform_file(file: FileTypes) -> HttpxFileTypes: if is_file_content(file): if isinstance(file, os.PathLike): path = pathlib.Path(file) return (path.name, path.read_bytes()) return file if is_tuple_t(file): return (file[0], read_file_content(file[1]), *file[2:]) raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple") def read_file_content(file: FileContent) -> HttpxFileContent: if isinstance(file, os.PathLike): return pathlib.Path(file).read_bytes() return file @overload async def async_to_httpx_files(files: None) -> None: ... @overload async def async_to_httpx_files(files: RequestFiles) -> HttpxRequestFiles: ... async def async_to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles | None: if files is None: return None if is_mapping_t(files): files = {key: await _async_transform_file(file) for key, file in files.items()} elif is_sequence_t(files): files = [(key, await _async_transform_file(file)) for key, file in files] else: raise TypeError(f"Unexpected file type input {type(files)}, expected mapping or sequence") return files async def _async_transform_file(file: FileTypes) -> HttpxFileTypes: if is_file_content(file): if isinstance(file, os.PathLike): path = anyio.Path(file) return (path.name, await path.read_bytes()) return file if is_tuple_t(file): return (file[0], await async_read_file_content(file[1]), *file[2:]) raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple") async def async_read_file_content(file: FileContent) -> HttpxFileContent: if isinstance(file, os.PathLike): return await anyio.Path(file).read_bytes() return file def deepcopy_with_paths(item: _T, paths: Sequence[Sequence[str]]) -> _T: """Copy only the containers along the given paths. Used to guard against mutation by extract_files without copying the entire structure. Only dicts and lists that lie on a path are copied; everything else is returned by reference. For example, given paths=[["foo", "files", "file"]] and the structure: { "foo": { "bar": {"baz": {}}, "files": {"file": } } } The root dict, "foo", and "files" are copied (they lie on the path). "bar" and "baz" are returned by reference (off the path). """ return _deepcopy_with_paths(item, paths, 0) def _deepcopy_with_paths(item: _T, paths: Sequence[Sequence[str]], index: int) -> _T: if not paths: return item if is_mapping(item): key_to_paths: dict[str, list[Sequence[str]]] = {} for path in paths: if index < len(path): key_to_paths.setdefault(path[index], []).append(path) # if no path continues through this mapping, it won't be mutated and copying it is redundant if not key_to_paths: return item result = dict(item) for key, subpaths in key_to_paths.items(): if key in result: result[key] = _deepcopy_with_paths(result[key], subpaths, index + 1) return cast(_T, result) if is_list(item): array_paths = [path for path in paths if index < len(path) and path[index] == ""] # if no path expects a list here, nothing will be mutated inside it - return by reference if not array_paths: return cast(_T, item) return cast(_T, [_deepcopy_with_paths(entry, array_paths, index + 1) for entry in item]) return item anthropic-sdk-python-0.120.2/src/anthropic/_legacy_response.py000066400000000000000000000417341523216435200244260ustar00rootroot00000000000000from __future__ import annotations import os import inspect import logging import datetime import functools from typing import ( TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, Iterator, AsyncIterator, cast, overload, ) from typing_extensions import Awaitable, ParamSpec, override, deprecated, get_origin import anyio import httpx import pydantic from ._types import NoneType from ._utils import is_given, extract_type_arg, is_annotated_type, is_type_alias_type from ._models import BaseModel, is_basemodel, add_request_id from ._constants import RAW_RESPONSE_HEADER from ._streaming import Stream, AsyncStream, is_stream_class_type, extract_stream_chunk_type from ._exceptions import APIResponseValidationError from ._decoders.jsonl import JSONLDecoder, AsyncJSONLDecoder if TYPE_CHECKING: from ._models import FinalRequestOptions from ._base_client import BaseClient P = ParamSpec("P") R = TypeVar("R") _T = TypeVar("_T") _T_co = TypeVar("_T_co", covariant=True) log: logging.Logger = logging.getLogger(__name__) class LegacyAPIResponse(Generic[R]): """This is a legacy class as it will be replaced by `APIResponse` and `AsyncAPIResponse` in the `_response.py` file in the next major release. For the sync client this will mostly be the same with the exception of `content` & `text` will be methods instead of properties. In the async client, all methods will be async. A migration script will be provided & the migration in general should be smooth. """ _cast_to: type[R] _client: BaseClient[Any, Any] _parsed_by_type: dict[type[Any], Any] _stream: bool _stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None _options: FinalRequestOptions http_response: httpx.Response retries_taken: int """The number of retries made. If no retries happened this will be `0`""" def __init__( self, *, raw: httpx.Response, cast_to: type[R], client: BaseClient[Any, Any], stream: bool, stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, options: FinalRequestOptions, retries_taken: int = 0, ) -> None: self._cast_to = cast_to self._client = client self._parsed_by_type = {} self._stream = stream self._stream_cls = stream_cls self._options = options self.http_response = raw self.retries_taken = retries_taken @property def request_id(self) -> str | None: return self.http_response.headers.get("request-id") # type: ignore[no-any-return] @overload def parse(self, *, to: type[_T]) -> _T: ... @overload def parse(self) -> R: ... def parse(self, *, to: type[_T] | None = None) -> R | _T: """Returns the rich python representation of this response's data. NOTE: For the async client: this will become a coroutine in the next major version. For lower-level control, see `.read()`, `.json()`, `.iter_bytes()`. You can customise the type that the response is parsed into through the `to` argument, e.g. ```py from anthropic import BaseModel class MyModel(BaseModel): foo: str obj = response.parse(to=MyModel) print(obj.foo) ``` We support parsing: - `BaseModel` - `dict` - `list` - `Union` - `str` - `int` - `float` - `httpx.Response` """ cache_key = to if to is not None else self._cast_to cached = self._parsed_by_type.get(cache_key) if cached is not None: return cached # type: ignore[no-any-return] parsed = self._parse(to=to) if is_given(self._options.post_parser): parsed = self._options.post_parser(parsed) if isinstance(parsed, BaseModel): add_request_id(parsed, self.request_id) self._parsed_by_type[cache_key] = parsed return cast(R, parsed) @property def headers(self) -> httpx.Headers: return self.http_response.headers @property def http_request(self) -> httpx.Request: return self.http_response.request @property def status_code(self) -> int: return self.http_response.status_code @property def url(self) -> httpx.URL: return self.http_response.url @property def method(self) -> str: return self.http_request.method @property def content(self) -> bytes: """Return the binary response content. NOTE: this will be removed in favour of `.read()` in the next major version. """ return self.http_response.content @property def text(self) -> str: """Return the decoded response content. NOTE: this will be turned into a method in the next major version. """ return self.http_response.text @property def http_version(self) -> str: return self.http_response.http_version @property def is_closed(self) -> bool: return self.http_response.is_closed @property def elapsed(self) -> datetime.timedelta: """The time taken for the complete request/response cycle to complete.""" return self.http_response.elapsed def _parse(self, *, to: type[_T] | None = None) -> R | _T: cast_to = to if to is not None else self._cast_to # unwrap `TypeAlias('Name', T)` -> `T` if is_type_alias_type(cast_to): cast_to = cast_to.__value__ # type: ignore[unreachable] # unwrap `Annotated[T, ...]` -> `T` if cast_to and is_annotated_type(cast_to): cast_to = extract_type_arg(cast_to, 0) origin = get_origin(cast_to) or cast_to if inspect.isclass(origin): if issubclass(cast(Any, origin), JSONLDecoder): return cast( R, cast("type[JSONLDecoder[Any]]", cast_to)( raw_iterator=self.http_response.iter_bytes(chunk_size=64), line_type=extract_type_arg(cast_to, 0), http_response=self.http_response, ), ) if issubclass(cast(Any, origin), AsyncJSONLDecoder): return cast( R, cast("type[AsyncJSONLDecoder[Any]]", cast_to)( raw_iterator=self.http_response.aiter_bytes(chunk_size=64), line_type=extract_type_arg(cast_to, 0), http_response=self.http_response, ), ) if self._stream: if to: if not is_stream_class_type(to): raise TypeError(f"Expected custom parse type to be a subclass of {Stream} or {AsyncStream}") return cast( _T, to( cast_to=extract_stream_chunk_type( to, failure_message="Expected custom stream type to be passed with a type argument, e.g. Stream[ChunkType]", ), response=self.http_response, client=cast(Any, self._client), options=self._options, ), ) if self._stream_cls: return cast( R, self._stream_cls( cast_to=extract_stream_chunk_type(self._stream_cls), response=self.http_response, client=cast(Any, self._client), options=self._options, ), ) stream_cls = cast("type[Stream[Any]] | type[AsyncStream[Any]] | None", self._client._default_stream_cls) if stream_cls is None: raise MissingStreamClassError() return cast( R, stream_cls( cast_to=cast_to, response=self.http_response, client=cast(Any, self._client), options=self._options, ), ) if cast_to is NoneType: return cast(R, None) response = self.http_response if cast_to == str: return cast(R, response.text) if cast_to == int: return cast(R, int(response.text)) if cast_to == float: return cast(R, float(response.text)) if cast_to == bool: return cast(R, response.text.lower() == "true") if inspect.isclass(origin) and issubclass(origin, HttpxBinaryResponseContent): return cast(R, cast_to(response)) # type: ignore if origin == LegacyAPIResponse: raise RuntimeError("Unexpected state - cast_to is `APIResponse`") if inspect.isclass( origin # pyright: ignore[reportUnknownArgumentType] ) and issubclass(origin, httpx.Response): # Because of the invariance of our ResponseT TypeVar, users can subclass httpx.Response # and pass that class to our request functions. We cannot change the variance to be either # covariant or contravariant as that makes our usage of ResponseT illegal. We could construct # the response class ourselves but that is something that should be supported directly in httpx # as it would be easy to incorrectly construct the Response object due to the multitude of arguments. if cast_to != httpx.Response: raise ValueError(f"Subclasses of httpx.Response cannot be passed to `cast_to`") return cast(R, response) if ( inspect.isclass( origin # pyright: ignore[reportUnknownArgumentType] ) and not issubclass(origin, BaseModel) and issubclass(origin, pydantic.BaseModel) ): raise TypeError("Pydantic models must subclass our base model type, e.g. `from anthropic import BaseModel`") if ( cast_to is not object and not origin is list and not origin is dict and not origin is Union and not issubclass(origin, BaseModel) ): raise RuntimeError( f"Unsupported type, expected {cast_to} to be a subclass of {BaseModel}, {dict}, {list}, {Union}, {NoneType}, {str} or {httpx.Response}." ) # split is required to handle cases where additional information is included # in the response, e.g. application/json; charset=utf-8 content_type, *_ = response.headers.get("content-type", "*").split(";") if not content_type.endswith("json"): if is_basemodel(cast_to): try: data = response.json() except Exception as exc: log.debug("Could not read JSON from response data due to %s - %s", type(exc), exc) else: return self._client._process_response_data( data=data, cast_to=cast_to, # type: ignore response=response, ) if self._client._strict_response_validation: raise APIResponseValidationError( response=response, message=f"Expected Content-Type response header to be `application/json` but received `{content_type}` instead.", body=response.text, ) # If the API responds with content that isn't JSON then we just return # the (decoded) text without performing any parsing so that you can still # handle the response however you need to. return response.text # type: ignore data = response.json() return self._client._process_response_data( data=data, cast_to=cast_to, # type: ignore response=response, ) @override def __repr__(self) -> str: return f"" class MissingStreamClassError(TypeError): def __init__(self) -> None: super().__init__( "The `stream` argument was set to `True` but the `stream_cls` argument was not given. See `anthropic._streaming` for reference", ) def to_raw_response_wrapper(func: Callable[P, R]) -> Callable[P, LegacyAPIResponse[R]]: """Higher order function that takes one of our bound API methods and wraps it to support returning the raw `APIResponse` object directly. """ @functools.wraps(func) def wrapped(*args: P.args, **kwargs: P.kwargs) -> LegacyAPIResponse[R]: extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "true" kwargs["extra_headers"] = extra_headers return cast(LegacyAPIResponse[R], func(*args, **kwargs)) return wrapped def async_to_raw_response_wrapper(func: Callable[P, Awaitable[R]]) -> Callable[P, Awaitable[LegacyAPIResponse[R]]]: """Higher order function that takes one of our bound API methods and wraps it to support returning the raw `APIResponse` object directly. """ @functools.wraps(func) async def wrapped(*args: P.args, **kwargs: P.kwargs) -> LegacyAPIResponse[R]: extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "true" kwargs["extra_headers"] = extra_headers return cast(LegacyAPIResponse[R], await func(*args, **kwargs)) return wrapped class HttpxBinaryResponseContent: response: httpx.Response def __init__(self, response: httpx.Response) -> None: self.response = response @property def content(self) -> bytes: return self.response.content @property def text(self) -> str: return self.response.text @property def encoding(self) -> str | None: return self.response.encoding @property def charset_encoding(self) -> str | None: return self.response.charset_encoding def json(self, **kwargs: Any) -> Any: return self.response.json(**kwargs) def read(self) -> bytes: return self.response.read() def iter_bytes(self, chunk_size: int | None = None) -> Iterator[bytes]: return self.response.iter_bytes(chunk_size) def iter_text(self, chunk_size: int | None = None) -> Iterator[str]: return self.response.iter_text(chunk_size) def iter_lines(self) -> Iterator[str]: return self.response.iter_lines() def iter_raw(self, chunk_size: int | None = None) -> Iterator[bytes]: return self.response.iter_raw(chunk_size) def write_to_file( self, file: str | os.PathLike[str], ) -> None: """Write the output to the given file. Accepts a filename or any path-like object, e.g. pathlib.Path Note: if you want to stream the data to the file instead of writing all at once then you should use `.with_streaming_response` when making the API request, e.g. `client.with_streaming_response.foo().stream_to_file('my_filename.txt')` """ with open(file, mode="wb") as f: for data in self.response.iter_bytes(): f.write(data) @deprecated( "Due to a bug, this method doesn't actually stream the response content, `.with_streaming_response.method()` should be used instead" ) def stream_to_file( self, file: str | os.PathLike[str], *, chunk_size: int | None = None, ) -> None: with open(file, mode="wb") as f: for data in self.response.iter_bytes(chunk_size): f.write(data) def close(self) -> None: return self.response.close() async def aread(self) -> bytes: return await self.response.aread() async def aiter_bytes(self, chunk_size: int | None = None) -> AsyncIterator[bytes]: return self.response.aiter_bytes(chunk_size) async def aiter_text(self, chunk_size: int | None = None) -> AsyncIterator[str]: return self.response.aiter_text(chunk_size) async def aiter_lines(self) -> AsyncIterator[str]: return self.response.aiter_lines() async def aiter_raw(self, chunk_size: int | None = None) -> AsyncIterator[bytes]: return self.response.aiter_raw(chunk_size) @deprecated( "Due to a bug, this method doesn't actually stream the response content, `.with_streaming_response.method()` should be used instead" ) async def astream_to_file( self, file: str | os.PathLike[str], *, chunk_size: int | None = None, ) -> None: path = anyio.Path(file) async with await path.open(mode="wb") as f: async for data in self.response.aiter_bytes(chunk_size): await f.write(data) async def aclose(self) -> None: return await self.response.aclose() anthropic-sdk-python-0.120.2/src/anthropic/_middleware.py000066400000000000000000000124641523216435200233570ustar00rootroot00000000000000from __future__ import annotations import inspect from typing import TYPE_CHECKING, Any, Union, Callable, Iterable, Awaitable from typing_extensions import TypeAlias from ._request import APIRequest if TYPE_CHECKING: from ._response import APIResponse, AsyncAPIResponse __all__ = [ "Middleware", "CallNext", "AsyncCallNext", "MiddlewareCallable", "AsyncMiddlewareCallable", "MiddlewareInput", ] CallNext: TypeAlias = Callable[[APIRequest], "APIResponse[Any]"] """Invokes the rest of the middleware chain and, ultimately, a single HTTP attempt. The middleware chain runs inside the SDK's retry loop, once per attempt, and returns the `APIResponse` for every HTTP response, including 4xx/5xx — inspect `response.status_code` to react to API errors; the SDK raises its typed errors for the original caller after the chain. Connection failures have no response to return, so they raise (`APITimeoutError`, `APIConnectionError`). Returns the `APIResponse` wrapper; call `.parse()` on it to get the typed model. """ AsyncCallNext: TypeAlias = Callable[[APIRequest], Awaitable["AsyncAPIResponse[Any]"]] """Invokes the rest of the middleware chain and, ultimately, a single HTTP attempt. The middleware chain runs inside the SDK's retry loop, once per attempt, and returns the `AsyncAPIResponse` for every HTTP response, including 4xx/5xx — inspect `response.status_code` to react to API errors; the SDK raises its typed errors for the original caller after the chain. Connection failures have no response to return, so they raise (`APITimeoutError`, `APIConnectionError`). Returns the `AsyncAPIResponse` wrapper; call `await .parse()` on it to get the typed model. """ class Middleware: """Base class for client-level middleware. Subclass and override `handle` (used by the sync client) and/or `handle_async` (used by the async client). The default implementations delegate straight to the rest of the chain. """ def handle(self, request: APIRequest, call_next: CallNext) -> APIResponse[Any]: return call_next(request) async def handle_async(self, request: APIRequest, call_next: AsyncCallNext) -> AsyncAPIResponse[Any]: return await call_next(request) MiddlewareCallable: TypeAlias = Callable[[APIRequest, CallNext], "APIResponse[Any]"] AsyncMiddlewareCallable: TypeAlias = Callable[[APIRequest, AsyncCallNext], Awaitable["AsyncAPIResponse[Any]"]] MiddlewareInput: TypeAlias = Union[Middleware, MiddlewareCallable, AsyncMiddlewareCallable] def _middleware_name(middleware: object) -> str: if isinstance(middleware, Middleware): return type(middleware).__name__ name = getattr(middleware, "__name__", None) return name if isinstance(name, str) else repr(middleware) def _is_async_callable(obj: object) -> bool: """Whether calling the given object returns a coroutine. Unlike `inspect.iscoroutinefunction(obj)` this also handles class instances that define an async `__call__` method. """ if inspect.iscoroutinefunction(obj): return True call = getattr(obj, "__call__", None) # noqa: B004 return call is not None and inspect.iscoroutinefunction(call) def validate_sync_middleware(middleware: Iterable[MiddlewareInput]) -> None: for entry in middleware: if isinstance(entry, Middleware): if type(entry).handle is Middleware.handle: raise TypeError( f"middleware {_middleware_name(entry)} does not implement `handle()`; " "the synchronous client requires sync-capable middleware" ) if inspect.iscoroutinefunction(type(entry).handle): raise TypeError( f"middleware {_middleware_name(entry)} defines `handle()` as an async function; " "the synchronous client requires `handle()` to be a sync function" ) elif not callable(entry): raise TypeError(f"middleware {_middleware_name(entry)} is not callable") elif _is_async_callable(entry): raise TypeError( f"middleware {_middleware_name(entry)} is an async function; " "the synchronous client requires sync middleware functions" ) def validate_async_middleware(middleware: Iterable[MiddlewareInput]) -> None: for entry in middleware: if isinstance(entry, Middleware): if type(entry).handle_async is Middleware.handle_async: raise TypeError( f"middleware {_middleware_name(entry)} does not implement `handle_async()`; " "the asynchronous client requires async-capable middleware" ) if not inspect.iscoroutinefunction(type(entry).handle_async): raise TypeError( f"middleware {_middleware_name(entry)} defines `handle_async()` as a sync function; " "the asynchronous client requires `handle_async()` to be an async function" ) elif not callable(entry): raise TypeError(f"middleware {_middleware_name(entry)} is not callable") elif not _is_async_callable(entry): raise TypeError( f"middleware {_middleware_name(entry)} is not an async function; " "the asynchronous client requires async middleware functions" ) anthropic-sdk-python-0.120.2/src/anthropic/_models.py000066400000000000000000001076411523216435200225270ustar00rootroot00000000000000from __future__ import annotations import os import inspect import weakref from typing import ( IO, TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, Iterable, Optional, AsyncIterable, cast, ) from datetime import date, datetime from typing_extensions import ( List, Unpack, Literal, ClassVar, Protocol, Required, Annotated, ParamSpec, TypeAlias, TypedDict, TypeGuard, final, override, runtime_checkable, ) import pydantic from pydantic.fields import FieldInfo from ._types import ( Body, IncEx, Query, ModelT, Headers, Timeout, NotGiven, AnyMapping, HttpxRequestFiles, ) from ._utils import ( PropertyInfo, is_list, is_given, json_safe, lru_cache, is_mapping, parse_date, coerce_boolean, parse_datetime, strip_not_given, extract_type_arg, is_annotated_type, is_type_alias_type, strip_annotated_type, ) from ._compat import ( PYDANTIC_V1, ConfigDict, GenericModel as BaseGenericModel, get_args, is_union, parse_obj, get_origin, is_literal_type, get_model_config, get_model_fields, field_get_default, ) from ._constants import RAW_RESPONSE_HEADER if TYPE_CHECKING: from pydantic import GetCoreSchemaHandler, ValidatorFunctionWrapHandler from pydantic_core import CoreSchema, core_schema from pydantic_core.core_schema import ModelField, ModelSchema, LiteralSchema, ModelFieldsSchema else: try: from pydantic_core import CoreSchema, core_schema except ImportError: CoreSchema = None core_schema = None __all__ = ["BaseModel", "GenericModel"] _T = TypeVar("_T") _BaseModelT = TypeVar("_BaseModelT", bound="BaseModel") P = ParamSpec("P") @runtime_checkable class _ConfigProtocol(Protocol): allow_population_by_field_name: bool class BaseModel(pydantic.BaseModel): if PYDANTIC_V1: @property @override def model_fields_set(self) -> set[str]: # a forwards-compat shim for pydantic v2 return self.__fields_set__ # type: ignore class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated] extra: Any = pydantic.Extra.allow # type: ignore else: model_config: ClassVar[ConfigDict] = ConfigDict( extra="allow", defer_build=coerce_boolean(os.environ.get("DEFER_PYDANTIC_BUILD", "true")) ) if TYPE_CHECKING: _request_id: Optional[str] = None """The ID of the request, returned via the `request-id` header. Useful for debugging requests and reporting issues to Anthropic. This will **only** be set for the top-level response object, it will not be defined for nested objects. For example: ```py message = await client.messages.create(...) message._request_id # req_xxx message.usage._request_id # raises `AttributeError` ``` Note: unlike other properties that use an `_` prefix, this property *is* public. Unless documented otherwise, all other `_` prefix properties, methods and modules are *private*. """ def to_dict( self, *, mode: Literal["json", "python"] = "python", use_api_names: bool = True, exclude_unset: bool = True, exclude_defaults: bool = False, exclude_none: bool = False, warnings: bool = True, ) -> dict[str, object]: """Recursively generate a dictionary representation of the model, optionally specifying which fields to include or exclude. By default, fields that were not set by the API will not be included, and keys will match the API response, *not* the property names from the model. For example, if the API responds with `"fooBar": true` but we've defined a `foo_bar: bool` property, the output will use the `"fooBar"` key (unless `use_api_names=False` is passed). Args: mode: If mode is 'json', the dictionary will only contain JSON serializable types. e.g. `datetime` will be turned into a string, `"2024-3-22T18:11:19.117000Z"`. If mode is 'python', the dictionary may contain any Python objects. e.g. `datetime(2024, 3, 22)` use_api_names: Whether to use the key that the API responded with or the property name. Defaults to `True`. exclude_unset: Whether to exclude fields that have not been explicitly set. exclude_defaults: Whether to exclude fields that are set to their default value from the output. exclude_none: Whether to exclude fields that have a value of `None` from the output. warnings: Whether to log warnings when invalid fields are encountered. This is only supported in Pydantic v2. """ return self.model_dump( mode=mode, by_alias=use_api_names, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, exclude_none=exclude_none, warnings=warnings, ) def to_json( self, *, indent: int | None = 2, use_api_names: bool = True, exclude_unset: bool = True, exclude_defaults: bool = False, exclude_none: bool = False, warnings: bool = True, ) -> str: """Generates a JSON string representing this model as it would be received from or sent to the API (but with indentation). By default, fields that were not set by the API will not be included, and keys will match the API response, *not* the property names from the model. For example, if the API responds with `"fooBar": true` but we've defined a `foo_bar: bool` property, the output will use the `"fooBar"` key (unless `use_api_names=False` is passed). Args: indent: Indentation to use in the JSON output. If `None` is passed, the output will be compact. Defaults to `2` use_api_names: Whether to use the key that the API responded with or the property name. Defaults to `True`. exclude_unset: Whether to exclude fields that have not been explicitly set. exclude_defaults: Whether to exclude fields that have the default value. exclude_none: Whether to exclude fields that have a value of `None`. warnings: Whether to show any warnings that occurred during serialization. This is only supported in Pydantic v2. """ return self.model_dump_json( indent=indent, by_alias=use_api_names, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, exclude_none=exclude_none, warnings=warnings, ) @override def __str__(self) -> str: # mypy complains about an invalid self arg return f"{self.__repr_name__()}({self.__repr_str__(', ')})" # type: ignore[misc] # Override the 'construct' method in a way that supports recursive parsing without validation. # Based on https://github.com/samuelcolvin/pydantic/issues/1168#issuecomment-817742836. @classmethod @override def construct( # pyright: ignore[reportIncompatibleMethodOverride] __cls: Type[ModelT], _fields_set: set[str] | None = None, **values: object, ) -> ModelT: m = __cls.__new__(__cls) fields_values: dict[str, object] = {} config = get_model_config(__cls) populate_by_name = ( config.allow_population_by_field_name if isinstance(config, _ConfigProtocol) else config.get("populate_by_name") ) if _fields_set is None: _fields_set = set() model_fields = get_model_fields(__cls) for name, field in model_fields.items(): key = field.alias if key is None or (key not in values and populate_by_name): key = name if key in values: fields_values[name] = _construct_field(value=values[key], field=field, key=key) _fields_set.add(name) else: fields_values[name] = field_get_default(field) extra_field_type = _get_extra_fields_type(__cls) _extra = {} for key, value in values.items(): if key not in model_fields: parsed = construct_type(value=value, type_=extra_field_type) if extra_field_type is not None else value if PYDANTIC_V1: _fields_set.add(key) fields_values[key] = parsed else: _extra[key] = parsed object.__setattr__(m, "__dict__", fields_values) if PYDANTIC_V1: # init_private_attributes() does not exist in v2 m._init_private_attributes() # type: ignore # copied from Pydantic v1's `construct()` method object.__setattr__(m, "__fields_set__", _fields_set) else: # these properties are copied from Pydantic's `model_construct()` method object.__setattr__(m, "__pydantic_private__", None) object.__setattr__(m, "__pydantic_extra__", _extra) object.__setattr__(m, "__pydantic_fields_set__", _fields_set) return m if not TYPE_CHECKING: # type checkers incorrectly complain about this assignment # because the type signatures are technically different # although not in practice model_construct = construct if PYDANTIC_V1: # we define aliases for some of the new pydantic v2 methods so # that we can just document these methods without having to specify # a specific pydantic version as some users may not know which # pydantic version they are currently using @override def model_dump( self, *, mode: Literal["json", "python"] | str = "python", include: IncEx | None = None, exclude: IncEx | None = None, context: Any | None = None, by_alias: bool | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, exclude_computed_fields: bool = False, round_trip: bool = False, warnings: bool | Literal["none", "warn", "error"] = True, fallback: Callable[[Any], Any] | None = None, serialize_as_any: bool = False, ) -> dict[str, Any]: """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump Generate a dictionary representation of the model, optionally specifying which fields to include or exclude. Args: mode: The mode in which `to_python` should run. If mode is 'json', the output will only contain JSON serializable types. If mode is 'python', the output may contain non-JSON-serializable Python objects. include: A set of fields to include in the output. exclude: A set of fields to exclude from the output. context: Additional context to pass to the serializer. by_alias: Whether to use the field's alias in the dictionary key if defined. exclude_unset: Whether to exclude fields that have not been explicitly set. exclude_defaults: Whether to exclude fields that are set to their default value. exclude_none: Whether to exclude fields that have a value of `None`. exclude_computed_fields: Whether to exclude computed fields. While this can be useful for round-tripping, it is usually recommended to use the dedicated `round_trip` parameter instead. round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T]. warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors, "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError]. fallback: A function to call when an unknown value is encountered. If not provided, a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised. serialize_as_any: Whether to serialize fields with duck-typing serialization behavior. Returns: A dictionary representation of the model. """ if mode not in {"json", "python"}: raise ValueError("mode must be either 'json' or 'python'") if round_trip != False: raise ValueError("round_trip is only supported in Pydantic v2") if warnings != True: raise ValueError("warnings is only supported in Pydantic v2") if context is not None: raise ValueError("context is only supported in Pydantic v2") if serialize_as_any != False: raise ValueError("serialize_as_any is only supported in Pydantic v2") if fallback is not None: raise ValueError("fallback is only supported in Pydantic v2") if exclude_computed_fields != False: raise ValueError("exclude_computed_fields is only supported in Pydantic v2") dumped = super().dict( # pyright: ignore[reportDeprecated] include=include, exclude=exclude, by_alias=by_alias if by_alias is not None else False, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, exclude_none=exclude_none, ) return cast("dict[str, Any]", json_safe(dumped)) if mode == "json" else dumped @override def model_dump_json( self, *, indent: int | None = None, ensure_ascii: bool = False, include: IncEx | None = None, exclude: IncEx | None = None, context: Any | None = None, by_alias: bool | None = None, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, exclude_computed_fields: bool = False, round_trip: bool = False, warnings: bool | Literal["none", "warn", "error"] = True, fallback: Callable[[Any], Any] | None = None, serialize_as_any: bool = False, ) -> str: """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump_json Generates a JSON representation of the model using Pydantic's `to_json` method. Args: indent: Indentation to use in the JSON output. If None is passed, the output will be compact. include: Field(s) to include in the JSON output. Can take either a string or set of strings. exclude: Field(s) to exclude from the JSON output. Can take either a string or set of strings. by_alias: Whether to serialize using field aliases. exclude_unset: Whether to exclude fields that have not been explicitly set. exclude_defaults: Whether to exclude fields that have the default value. exclude_none: Whether to exclude fields that have a value of `None`. round_trip: Whether to use serialization/deserialization between JSON and class instance. warnings: Whether to show any warnings that occurred during serialization. Returns: A JSON string representation of the model. """ if round_trip != False: raise ValueError("round_trip is only supported in Pydantic v2") if warnings != True: raise ValueError("warnings is only supported in Pydantic v2") if context is not None: raise ValueError("context is only supported in Pydantic v2") if serialize_as_any != False: raise ValueError("serialize_as_any is only supported in Pydantic v2") if fallback is not None: raise ValueError("fallback is only supported in Pydantic v2") if ensure_ascii != False: raise ValueError("ensure_ascii is only supported in Pydantic v2") if exclude_computed_fields != False: raise ValueError("exclude_computed_fields is only supported in Pydantic v2") return super().json( # type: ignore[reportDeprecated] indent=indent, include=include, exclude=exclude, by_alias=by_alias if by_alias is not None else False, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, exclude_none=exclude_none, ) class _EagerIterable(list[_T], Generic[_T]): """ Accepts any Iterable[T] input (including generators), consumes it eagerly, and validates all items upfront. Validation preserves the original container type where possible (e.g. a set[T] stays a set[T]). Serialization (model_dump / JSON) always emits a list — round-tripping through model_dump() will not restore the original container type. """ @classmethod def __get_pydantic_core_schema__( cls, source_type: Any, handler: GetCoreSchemaHandler, ) -> CoreSchema: (item_type,) = get_args(source_type) or (Any,) item_schema: CoreSchema = handler.generate_schema(item_type) list_of_items_schema: CoreSchema = core_schema.list_schema(item_schema) return core_schema.no_info_wrap_validator_function( cls._validate, list_of_items_schema, serialization=core_schema.plain_serializer_function_ser_schema( cls._serialize, info_arg=False, ), ) @staticmethod def _validate(v: Iterable[_T], handler: "ValidatorFunctionWrapHandler") -> Any: original_type: type[Any] = type(v) # Normalize to list so list_schema can validate each item if isinstance(v, list): items: list[_T] = v else: try: items = list(v) except TypeError as e: raise TypeError("Value is not iterable") from e # Validate items against the inner schema validated: list[_T] = handler(items) # Reconstruct original container type if original_type is list: return validated # str(list) produces the list's repr, not a string built from items, # so skip reconstruction for str and its subclasses. if issubclass(original_type, str): return validated try: return original_type(validated) except (TypeError, ValueError): # If the type cannot be reconstructed, just return the validated list return validated @staticmethod def _serialize(v: Iterable[_T]) -> list[_T]: """Always serialize as a list so Pydantic's JSON encoder is happy.""" if isinstance(v, list): return v return list(v) EagerIterable: TypeAlias = Annotated[Iterable[_T], _EagerIterable] def _construct_field(value: object, field: FieldInfo, key: str) -> object: if value is None: return field_get_default(field) if PYDANTIC_V1: type_ = cast(type, field.outer_type_) # type: ignore else: type_ = field.annotation # type: ignore if type_ is None: raise RuntimeError(f"Unexpected field type is None for {key}") return construct_type(value=value, type_=type_, metadata=getattr(field, "metadata", None)) def _get_extra_fields_type(cls: type[pydantic.BaseModel]) -> type | None: if PYDANTIC_V1: # TODO return None schema = cls.__pydantic_core_schema__ if schema["type"] == "model": fields = schema["schema"] if fields["type"] == "model-fields": extras = fields.get("extras_schema") if extras and "cls" in extras: # mypy can't narrow the type return extras["cls"] # type: ignore[no-any-return] return None def is_basemodel(type_: type) -> bool: """Returns whether or not the given type is either a `BaseModel` or a union of `BaseModel`""" if is_union(type_): for variant in get_args(type_): if is_basemodel(variant): return True return False return is_basemodel_type(type_) def is_basemodel_type(type_: type) -> TypeGuard[type[BaseModel] | type[GenericModel]]: origin = get_origin(type_) or type_ if not inspect.isclass(origin): return False return issubclass(origin, BaseModel) or issubclass(origin, GenericModel) def build( base_model_cls: Callable[P, _BaseModelT], *args: P.args, **kwargs: P.kwargs, ) -> _BaseModelT: """Construct a BaseModel class without validation. This is useful for cases where you need to instantiate a `BaseModel` from an API response as this provides type-safe params which isn't supported by helpers like `construct_type()`. ```py build(MyModel, my_field_a="foo", my_field_b=123) ``` """ if args: raise TypeError( "Received positional arguments which are not supported; Keyword arguments must be used instead", ) return cast(_BaseModelT, construct_type(type_=base_model_cls, value=kwargs)) def construct_type_unchecked(*, value: object, type_: type[_T]) -> _T: """Loose coercion to the expected type with construction of nested values. Note: the returned value from this function is not guaranteed to match the given type. """ return cast(_T, construct_type(value=value, type_=type_)) def construct_type(*, value: object, type_: object, metadata: Optional[List[Any]] = None) -> object: """Loose coercion to the expected type with construction of nested values. If the given value does not match the expected type then it is returned as-is. """ # store a reference to the original type we were given before we extract any inner # types so that we can properly resolve forward references in `TypeAliasType` annotations original_type = None # we allow `object` as the input type because otherwise, passing things like # `Literal['value']` will be reported as a type error by type checkers type_ = cast("type[object]", type_) if is_type_alias_type(type_): original_type = type_ # type: ignore[unreachable] type_ = type_.__value__ # type: ignore[unreachable] # unwrap `Annotated[T, ...]` -> `T` if metadata is not None and len(metadata) > 0: meta: tuple[Any, ...] = tuple(metadata) elif is_annotated_type(type_): meta = get_args(type_)[1:] type_ = extract_type_arg(type_, 0) else: meta = tuple() # we need to use the origin class for any types that are subscripted generics # e.g. Dict[str, object] origin = get_origin(type_) or type_ args = get_args(type_) if is_union(origin): try: return validate_type(type_=cast("type[object]", original_type or type_), value=value) except Exception: pass # if the type is a discriminated union then we want to construct the right variant # in the union, even if the data doesn't match exactly, otherwise we'd break code # that relies on the constructed class types, e.g. # # class FooType: # kind: Literal['foo'] # value: str # # class BarType: # kind: Literal['bar'] # value: int # # without this block, if the data we get is something like `{'kind': 'bar', 'value': 'foo'}` then # we'd end up constructing `FooType` when it should be `BarType`. discriminator = _build_discriminated_union_meta(union=type_, meta_annotations=meta) if discriminator and is_mapping(value): variant_value = value.get(discriminator.field_alias_from or discriminator.field_name) if variant_value and isinstance(variant_value, str): variant_type = discriminator.mapping.get(variant_value) if variant_type: return construct_type(type_=variant_type, value=value) # if the data is not valid, use the first variant that doesn't fail while deserializing for variant in args: try: return construct_type(value=value, type_=variant) except Exception: continue raise RuntimeError(f"Could not convert data into a valid instance of {type_}") if origin == dict: if not is_mapping(value): return value _, items_type = get_args(type_) # Dict[_, items_type] return {key: construct_type(value=item, type_=items_type) for key, item in value.items()} if ( not is_literal_type(type_) and inspect.isclass(origin) and (issubclass(origin, BaseModel) or issubclass(origin, GenericModel)) ): if is_list(value): return [cast(Any, type_).construct(**entry) if is_mapping(entry) else entry for entry in value] if is_mapping(value): if issubclass(type_, BaseModel): return type_.construct(**value) # type: ignore[arg-type] return cast(Any, type_).construct(**value) if origin == list: if not is_list(value): return value inner_type = args[0] # List[inner_type] return [construct_type(value=entry, type_=inner_type) for entry in value] if origin == float: if isinstance(value, int): coerced = float(value) if coerced != value: return value return coerced return value if type_ == datetime: try: return parse_datetime(value) # type: ignore except Exception: return value if type_ == date: try: return parse_date(value) # type: ignore except Exception: return value return value @runtime_checkable class CachedDiscriminatorType(Protocol): __discriminator__: DiscriminatorDetails DISCRIMINATOR_CACHE: weakref.WeakKeyDictionary[type, DiscriminatorDetails] = weakref.WeakKeyDictionary() class DiscriminatorDetails: field_name: str """The name of the discriminator field in the variant class, e.g. ```py class Foo(BaseModel): type: Literal['foo'] ``` Will result in field_name='type' """ field_alias_from: str | None """The name of the discriminator field in the API response, e.g. ```py class Foo(BaseModel): type: Literal['foo'] = Field(alias='type_from_api') ``` Will result in field_alias_from='type_from_api' """ mapping: dict[str, type] """Mapping of discriminator value to variant type, e.g. {'foo': FooVariant, 'bar': BarVariant} """ def __init__( self, *, mapping: dict[str, type], discriminator_field: str, discriminator_alias: str | None, ) -> None: self.mapping = mapping self.field_name = discriminator_field self.field_alias_from = discriminator_alias def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, ...]) -> DiscriminatorDetails | None: cached = DISCRIMINATOR_CACHE.get(union) if cached is not None: return cached discriminator_field_name: str | None = None for annotation in meta_annotations: if isinstance(annotation, PropertyInfo) and annotation.discriminator is not None: discriminator_field_name = annotation.discriminator break if not discriminator_field_name: return None mapping: dict[str, type] = {} discriminator_alias: str | None = None for variant in get_args(union): variant = strip_annotated_type(variant) if is_basemodel_type(variant): if PYDANTIC_V1: field_info = cast("dict[str, FieldInfo]", variant.__fields__).get(discriminator_field_name) # pyright: ignore[reportDeprecated, reportUnnecessaryCast] if not field_info: continue # Note: if one variant defines an alias then they all should discriminator_alias = field_info.alias if (annotation := getattr(field_info, "annotation", None)) and is_literal_type(annotation): for entry in get_args(annotation): if isinstance(entry, str): mapping[entry] = variant else: field = _extract_field_schema_pv2(variant, discriminator_field_name) if not field: continue # Note: if one variant defines an alias then they all should discriminator_alias = field.get("serialization_alias") field_schema = field["schema"] if field_schema["type"] == "literal": for entry in cast("LiteralSchema", field_schema)["expected"]: if isinstance(entry, str): mapping[entry] = variant if not mapping: return None details = DiscriminatorDetails( mapping=mapping, discriminator_field=discriminator_field_name, discriminator_alias=discriminator_alias, ) DISCRIMINATOR_CACHE.setdefault(union, details) return details def _extract_field_schema_pv2(model: type[BaseModel], field_name: str) -> ModelField | None: schema = model.__pydantic_core_schema__ if schema["type"] == "definitions": schema = schema["schema"] if schema["type"] != "model": return None schema = cast("ModelSchema", schema) fields_schema = schema["schema"] if fields_schema["type"] != "model-fields": return None fields_schema = cast("ModelFieldsSchema", fields_schema) field = fields_schema["fields"].get(field_name) if not field: return None return cast("ModelField", field) # pyright: ignore[reportUnnecessaryCast] def validate_type(*, type_: type[_T], value: object) -> _T: """Strict validation that the given value matches the expected type""" if inspect.isclass(type_) and issubclass(type_, pydantic.BaseModel): return cast(_T, parse_obj(type_, value)) return cast(_T, _validate_non_model_type(type_=type_, value=value)) def set_pydantic_config(typ: Any, config: pydantic.ConfigDict) -> None: """Add a pydantic config for the given type. Note: this is a no-op on Pydantic v1. """ setattr(typ, "__pydantic_config__", config) # noqa: B010 def add_request_id(obj: BaseModel, request_id: str | None) -> None: obj._request_id = request_id # in Pydantic v1, using setattr like we do above causes the attribute # to be included when serializing the model which we don't want in this # case so we need to explicitly exclude it if PYDANTIC_V1: try: exclude_fields = obj.__exclude_fields__ # type: ignore except AttributeError: cast(Any, obj).__exclude_fields__ = {"_request_id", "__exclude_fields__"} else: cast(Any, obj).__exclude_fields__ = {*(exclude_fields or {}), "_request_id", "__exclude_fields__"} # our use of subclassing here causes weirdness for type checkers, # so we just pretend that we don't subclass if TYPE_CHECKING: GenericModel = BaseModel else: class GenericModel(BaseGenericModel, BaseModel): pass if not PYDANTIC_V1: from pydantic import TypeAdapter as _TypeAdapter, computed_field as computed_field _CachedTypeAdapter = cast("TypeAdapter[object]", lru_cache(maxsize=None)(_TypeAdapter)) if TYPE_CHECKING: from pydantic import TypeAdapter else: TypeAdapter = _CachedTypeAdapter def _validate_non_model_type(*, type_: type[_T], value: object) -> _T: return TypeAdapter(type_).validate_python(value) elif not TYPE_CHECKING: # TODO: condition is weird class RootModel(GenericModel, Generic[_T]): """Used as a placeholder to easily convert runtime types to a Pydantic format to provide validation. For example: ```py validated = RootModel[int](__root__="5").__root__ # validated: 5 ``` """ __root__: _T def _validate_non_model_type(*, type_: type[_T], value: object) -> _T: model = _create_pydantic_model(type_).validate(value) return cast(_T, model.__root__) def _create_pydantic_model(type_: _T) -> Type[RootModel[_T]]: return RootModel[type_] # type: ignore def TypeAdapter(*_args: Any, **_kwargs: Any) -> Any: raise RuntimeError("attempted to use TypeAdapter in pydantic v1") def computed_field(func: Any | None = None, /, **__: Any) -> Any: def _exc_func(*_: Any, **__: Any) -> Any: raise RuntimeError("attempted to use computed_field in pydantic v1") def _dec(*_: Any, **__: Any) -> Any: return _exc_func if func is not None: return _dec(func) else: return _dec class FinalRequestOptionsInput(TypedDict, total=False): method: Required[str] url: Required[str] params: Query headers: Headers max_retries: int timeout: float | Timeout | None files: HttpxRequestFiles | None idempotency_key: str content: Union[bytes, bytearray, IO[bytes], Iterable[bytes], AsyncIterable[bytes], None] json_data: Body extra_json: AnyMapping follow_redirects: bool @final class FinalRequestOptions(pydantic.BaseModel): method: str url: str params: Query = {} headers: Union[Headers, NotGiven] = NotGiven() max_retries: Union[int, NotGiven] = NotGiven() timeout: Union[float, Timeout, None, NotGiven] = NotGiven() files: Union[HttpxRequestFiles, None] = None idempotency_key: Union[str, None] = None post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven() follow_redirects: Union[bool, None] = None content: Union[bytes, bytearray, IO[bytes], Iterable[bytes], AsyncIterable[bytes], None] = None # It should be noted that we cannot use `json` here as that would override # a BaseModel method in an incompatible fashion. json_data: Union[Body, None] = None extra_json: Union[AnyMapping, None] = None if PYDANTIC_V1: class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated] arbitrary_types_allowed: bool = True else: model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True) def get_max_retries(self, max_retries: int) -> int: if isinstance(self.max_retries, NotGiven): return max_retries return self.max_retries def _strip_raw_response_header(self) -> None: if not is_given(self.headers): return if self.headers.get(RAW_RESPONSE_HEADER): self.headers = {**self.headers} self.headers.pop(RAW_RESPONSE_HEADER) # override the `construct` method so that we can run custom transformations. # this is necessary as we don't want to do any actual runtime type checking # (which means we can't use validators) but we do want to ensure that `NotGiven` # values are not present # # type ignore required because we're adding explicit types to `**values` @classmethod def construct( # type: ignore cls, _fields_set: set[str] | None = None, **values: Unpack[FinalRequestOptionsInput], ) -> FinalRequestOptions: kwargs: dict[str, Any] = { # we unconditionally call `strip_not_given` on any value # as it will just ignore any non-mapping types key: strip_not_given(value) for key, value in values.items() } if PYDANTIC_V1: return cast(FinalRequestOptions, super().construct(_fields_set, **kwargs)) # pyright: ignore[reportDeprecated] return super().model_construct(_fields_set, **kwargs) if not TYPE_CHECKING: # type checkers incorrectly complain about this assignment model_construct = construct anthropic-sdk-python-0.120.2/src/anthropic/_qs.py000066400000000000000000000113411523216435200216560ustar00rootroot00000000000000from __future__ import annotations from typing import Any, List, Tuple, Union, Mapping, TypeVar from urllib.parse import parse_qs, urlencode from typing_extensions import get_args from ._types import NotGiven, ArrayFormat, NestedFormat, not_given from ._utils import flatten _T = TypeVar("_T") PrimitiveData = Union[str, int, float, bool, None] # this should be Data = Union[PrimitiveData, "List[Data]", "Tuple[Data]", "Mapping[str, Data]"] # https://github.com/microsoft/pyright/issues/3555 Data = Union[PrimitiveData, List[Any], Tuple[Any], "Mapping[str, Any]"] Params = Mapping[str, Data] class Querystring: array_format: ArrayFormat nested_format: NestedFormat def __init__( self, *, array_format: ArrayFormat = "repeat", nested_format: NestedFormat = "brackets", ) -> None: self.array_format = array_format self.nested_format = nested_format def parse(self, query: str) -> Mapping[str, object]: # Note: custom format syntax is not supported yet return parse_qs(query) def stringify( self, params: Params, *, array_format: ArrayFormat | NotGiven = not_given, nested_format: NestedFormat | NotGiven = not_given, ) -> str: return urlencode( self.stringify_items( params, array_format=array_format, nested_format=nested_format, ) ) def stringify_items( self, params: Params, *, array_format: ArrayFormat | NotGiven = not_given, nested_format: NestedFormat | NotGiven = not_given, ) -> list[tuple[str, str]]: opts = Options( qs=self, array_format=array_format, nested_format=nested_format, ) return flatten([self._stringify_item(key, value, opts) for key, value in params.items()]) def _stringify_item( self, key: str, value: Data, opts: Options, ) -> list[tuple[str, str]]: if isinstance(value, Mapping): items: list[tuple[str, str]] = [] nested_format = opts.nested_format for subkey, subvalue in value.items(): items.extend( self._stringify_item( # TODO: error if unknown format f"{key}.{subkey}" if nested_format == "dots" else f"{key}[{subkey}]", subvalue, opts, ) ) return items if isinstance(value, (list, tuple)): array_format = opts.array_format if array_format == "comma": return [ ( key, ",".join(self._primitive_value_to_str(item) for item in value if item is not None), ), ] elif array_format == "repeat": items = [] for item in value: items.extend(self._stringify_item(key, item, opts)) return items elif array_format == "indices": items = [] for i, item in enumerate(value): items.extend(self._stringify_item(f"{key}[{i}]", item, opts)) return items elif array_format == "brackets": items = [] key = key + "[]" for item in value: items.extend(self._stringify_item(key, item, opts)) return items else: raise NotImplementedError( f"Unknown array_format value: {array_format}, choose from {', '.join(get_args(ArrayFormat))}" ) serialised = self._primitive_value_to_str(value) if not serialised: return [] return [(key, serialised)] def _primitive_value_to_str(self, value: PrimitiveData) -> str: # copied from httpx if value is True: return "true" elif value is False: return "false" elif value is None: return "" return str(value) _qs = Querystring() parse = _qs.parse stringify = _qs.stringify stringify_items = _qs.stringify_items class Options: array_format: ArrayFormat nested_format: NestedFormat def __init__( self, qs: Querystring = _qs, *, array_format: ArrayFormat | NotGiven = not_given, nested_format: NestedFormat | NotGiven = not_given, ) -> None: self.array_format = qs.array_format if isinstance(array_format, NotGiven) else array_format self.nested_format = qs.nested_format if isinstance(nested_format, NotGiven) else nested_format anthropic-sdk-python-0.120.2/src/anthropic/_request.py000066400000000000000000000067271523216435200227370ustar00rootroot00000000000000from __future__ import annotations import copy as _copy from typing import Any from typing_extensions import override import httpx from ._types import Body, Query, Headers, NotGiven, not_given from ._utils import is_given from ._compat import model_copy from ._models import FinalRequestOptions class APIRequest: """A view over the request that the client is about to execute. Treat instances as immutable; use `copy()` to derive a modified request. """ def __init__( self, *, options: FinalRequestOptions, cast_to: Any, stream: bool = False, stream_cls: type[Any] | None = None, retries_taken: int = 0, ) -> None: self.options = options self.cast_to = cast_to self.stream = stream self.stream_cls = stream_cls self.retries_taken = retries_taken """The number of retries the SDK has already taken for this call. `0` on the first attempt; the middleware chain is invoked once per HTTP attempt. """ @property def method(self) -> str: return self.options.method @property def url(self) -> str: return self.options.url @property def headers(self) -> Headers: headers = self.options.headers return headers if is_given(headers) else {} @property def query_params(self) -> Query: return self.options.params @property def json(self) -> Body | None: return self.options.json_data @property def timeout(self) -> float | httpx.Timeout | None | NotGiven: return self.options.timeout @property def max_retries(self) -> int | NotGiven: return self.options.max_retries def copy( self, *, method: str | NotGiven = not_given, url: str | NotGiven = not_given, headers: Headers | NotGiven = not_given, params: Query | NotGiven = not_given, body: Body | NotGiven = not_given, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> APIRequest: # Note: we intentionally avoid `model_copy(deep=True)` here as fields like # `files` and `content` can hold open file/IO objects which cannot be deep-copied. # # Instead we shallow-copy the options and then deep-copy only the JSON-safe mutable # fields so that mutating the returned request never affects the original request. options = model_copy(self.options) options.json_data = _copy.deepcopy(options.json_data) options.extra_json = _copy.deepcopy(options.extra_json) options.params = _copy.deepcopy(options.params) if is_given(options.headers): options.headers = dict(options.headers) if is_given(method): options.method = method if is_given(url): options.url = url if is_given(headers): options.headers = headers if is_given(params): options.params = params if not isinstance(body, NotGiven): options.json_data = body if not isinstance(timeout, NotGiven): options.timeout = timeout return APIRequest( options=options, cast_to=self.cast_to, stream=self.stream, stream_cls=self.stream_cls, retries_taken=self.retries_taken, ) @override def __repr__(self) -> str: return f"" anthropic-sdk-python-0.120.2/src/anthropic/_resource.py000066400000000000000000000020701523216435200230610ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import time import anyio from ._base_client import SyncAPIClient, AsyncAPIClient class SyncAPIResource: _client: SyncAPIClient def __init__(self, client: SyncAPIClient) -> None: self._client = client self._get = client.get self._post = client.post self._patch = client.patch self._put = client.put self._delete = client.delete self._get_api_list = client.get_api_list def _sleep(self, seconds: float) -> None: time.sleep(seconds) class AsyncAPIResource: _client: AsyncAPIClient def __init__(self, client: AsyncAPIClient) -> None: self._client = client self._get = client.get self._post = client.post self._patch = client.patch self._put = client.put self._delete = client.delete self._get_api_list = client.get_api_list async def _sleep(self, seconds: float) -> None: await anyio.sleep(seconds) anthropic-sdk-python-0.120.2/src/anthropic/_response.py000066400000000000000000000737271523216435200231110ustar00rootroot00000000000000from __future__ import annotations import os import inspect import logging import datetime import functools from types import TracebackType from typing import ( TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, Iterator, AsyncIterator, cast, overload, ) from typing_extensions import Awaitable, ParamSpec, override, get_origin import anyio import httpx import pydantic from ._types import NoneType from ._utils import is_given, extract_type_arg, is_annotated_type, is_type_alias_type, extract_type_var_from_base from ._models import BaseModel, is_basemodel, add_request_id from ._constants import RAW_RESPONSE_HEADER, OVERRIDE_CAST_TO_HEADER from ._streaming import Stream, AsyncStream, is_stream_class_type, extract_stream_chunk_type from ._exceptions import AnthropicError, APIResponseValidationError from ._decoders.jsonl import JSONLDecoder, AsyncJSONLDecoder if TYPE_CHECKING: from ._models import FinalRequestOptions from ._base_client import BaseClient P = ParamSpec("P") R = TypeVar("R") _T = TypeVar("_T") _APIResponseT = TypeVar("_APIResponseT", bound="APIResponse[Any]") _AsyncAPIResponseT = TypeVar("_AsyncAPIResponseT", bound="AsyncAPIResponse[Any]") log: logging.Logger = logging.getLogger(__name__) class BaseAPIResponse(Generic[R]): _cast_to: type[R] _client: BaseClient[Any, Any] _parsed_by_type: dict[type[Any], Any] _is_sse_stream: bool _stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None _options: FinalRequestOptions http_response: httpx.Response retries_taken: int """The number of retries made. If no retries happened this will be `0`""" def __init__( self, *, raw: httpx.Response, cast_to: type[R], client: BaseClient[Any, Any], stream: bool, stream_cls: type[Stream[Any]] | type[AsyncStream[Any]] | None, options: FinalRequestOptions, retries_taken: int = 0, ) -> None: self._cast_to = cast_to self._client = client self._parsed_by_type = {} self._is_sse_stream = stream self._stream_cls = stream_cls self._options = options self.http_response = raw self.retries_taken = retries_taken @property def headers(self) -> httpx.Headers: return self.http_response.headers @property def http_request(self) -> httpx.Request: """Returns the httpx Request instance associated with the current response.""" return self.http_response.request @property def status_code(self) -> int: return self.http_response.status_code @property def url(self) -> httpx.URL: """Returns the URL for which the request was made.""" return self.http_response.url @property def method(self) -> str: return self.http_request.method @property def http_version(self) -> str: return self.http_response.http_version @property def elapsed(self) -> datetime.timedelta: """The time taken for the complete request/response cycle to complete.""" return self.http_response.elapsed @property def is_closed(self) -> bool: """Whether or not the response body has been closed. If this is False then there is response data that has not been read yet. You must either fully consume the response body or call `.close()` before discarding the response to prevent resource leaks. """ return self.http_response.is_closed @override def __repr__(self) -> str: return ( f"<{self.__class__.__name__} [{self.status_code} {self.http_response.reason_phrase}] type={self._cast_to}>" ) def _parse(self, *, to: type[_T] | None = None) -> R | _T: cast_to = to if to is not None else self._cast_to # unwrap `TypeAlias('Name', T)` -> `T` if is_type_alias_type(cast_to): cast_to = cast_to.__value__ # type: ignore[unreachable] # unwrap `Annotated[T, ...]` -> `T` if cast_to and is_annotated_type(cast_to): cast_to = extract_type_arg(cast_to, 0) origin = get_origin(cast_to) or cast_to if inspect.isclass(origin): if issubclass(cast(Any, origin), JSONLDecoder): return cast( R, cast("type[JSONLDecoder[Any]]", cast_to)( raw_iterator=self.http_response.iter_bytes(chunk_size=64), line_type=extract_type_arg(cast_to, 0), http_response=self.http_response, ), ) if issubclass(cast(Any, origin), AsyncJSONLDecoder): return cast( R, cast("type[AsyncJSONLDecoder[Any]]", cast_to)( raw_iterator=self.http_response.aiter_bytes(chunk_size=64), line_type=extract_type_arg(cast_to, 0), http_response=self.http_response, ), ) if self._is_sse_stream: if to: if not is_stream_class_type(to): raise TypeError(f"Expected custom parse type to be a subclass of {Stream} or {AsyncStream}") return cast( _T, to( cast_to=extract_stream_chunk_type( to, failure_message="Expected custom stream type to be passed with a type argument, e.g. Stream[ChunkType]", ), response=self.http_response, client=cast(Any, self._client), options=self._options, ), ) if self._stream_cls: return cast( R, self._stream_cls( cast_to=extract_stream_chunk_type(self._stream_cls), response=self.http_response, client=cast(Any, self._client), options=self._options, ), ) stream_cls = cast("type[Stream[Any]] | type[AsyncStream[Any]] | None", self._client._default_stream_cls) if stream_cls is None: raise MissingStreamClassError() return cast( R, stream_cls( cast_to=cast_to, response=self.http_response, client=cast(Any, self._client), options=self._options, ), ) if cast_to is NoneType: return cast(R, None) response = self.http_response if cast_to == str: return cast(R, response.text) if cast_to == bytes: return cast(R, response.content) if cast_to == int: return cast(R, int(response.text)) if cast_to == float: return cast(R, float(response.text)) if cast_to == bool: return cast(R, response.text.lower() == "true") # handle the legacy binary response case if inspect.isclass(cast_to) and cast_to.__name__ == "HttpxBinaryResponseContent": return cast(R, cast_to(response)) # type: ignore if origin == APIResponse: raise RuntimeError("Unexpected state - cast_to is `APIResponse`") if inspect.isclass( origin # pyright: ignore[reportUnknownArgumentType] ) and issubclass(origin, httpx.Response): # Because of the invariance of our ResponseT TypeVar, users can subclass httpx.Response # and pass that class to our request functions. We cannot change the variance to be either # covariant or contravariant as that makes our usage of ResponseT illegal. We could construct # the response class ourselves but that is something that should be supported directly in httpx # as it would be easy to incorrectly construct the Response object due to the multitude of arguments. if cast_to != httpx.Response: raise ValueError(f"Subclasses of httpx.Response cannot be passed to `cast_to`") return cast(R, response) if ( inspect.isclass( origin # pyright: ignore[reportUnknownArgumentType] ) and not issubclass(origin, BaseModel) and issubclass(origin, pydantic.BaseModel) ): raise TypeError("Pydantic models must subclass our base model type, e.g. `from anthropic import BaseModel`") if ( cast_to is not object and not origin is list and not origin is dict and not origin is Union and not issubclass(origin, BaseModel) ): raise RuntimeError( f"Unsupported type, expected {cast_to} to be a subclass of {BaseModel}, {dict}, {list}, {Union}, {NoneType}, {str} or {httpx.Response}." ) # split is required to handle cases where additional information is included # in the response, e.g. application/json; charset=utf-8 content_type, *_ = response.headers.get("content-type", "*").split(";") if not content_type.endswith("json"): if is_basemodel(cast_to): try: data = response.json() except Exception as exc: log.debug("Could not read JSON from response data due to %s - %s", type(exc), exc) else: return self._client._process_response_data( data=data, cast_to=cast_to, # type: ignore response=response, ) if self._client._strict_response_validation: raise APIResponseValidationError( response=response, message=f"Expected Content-Type response header to be `application/json` but received `{content_type}` instead.", body=response.text, ) # If the API responds with content that isn't JSON then we just return # the (decoded) text without performing any parsing so that you can still # handle the response however you need to. return response.text # type: ignore data = response.json() return self._client._process_response_data( data=data, cast_to=cast_to, # type: ignore response=response, ) class APIResponse(BaseAPIResponse[R]): @property def request_id(self) -> str | None: return self.http_response.headers.get("request-id") # type: ignore[no-any-return] @overload def parse(self, *, to: type[_T]) -> _T: ... @overload def parse(self) -> R: ... def parse(self, *, to: type[_T] | None = None) -> R | _T: """Returns the rich python representation of this response's data. For lower-level control, see `.read()`, `.json()`, `.iter_bytes()`. You can customise the type that the response is parsed into through the `to` argument, e.g. ```py from anthropic import BaseModel class MyModel(BaseModel): foo: str obj = response.parse(to=MyModel) print(obj.foo) ``` We support parsing: - `BaseModel` - `dict` - `list` - `Union` - `str` - `int` - `float` - `httpx.Response` """ cache_key = to if to is not None else self._cast_to cached = self._parsed_by_type.get(cache_key) if cached is not None: return cached # type: ignore[no-any-return] if not self._is_sse_stream: self.read() parsed = self._parse(to=to) if is_given(self._options.post_parser): parsed = self._options.post_parser(parsed) if isinstance(parsed, BaseModel): add_request_id(parsed, self.request_id) self._parsed_by_type[cache_key] = parsed return cast(R, parsed) def read(self) -> bytes: """Read and return the binary response content.""" try: return self.http_response.read() except httpx.StreamConsumed as exc: # The default error raised by httpx isn't very # helpful in our case so we re-raise it with # a different error message. raise StreamAlreadyConsumed() from exc def text(self) -> str: """Read and decode the response content into a string.""" self.read() return self.http_response.text def json(self) -> object: """Read and decode the JSON response content.""" self.read() return self.http_response.json() def close(self) -> None: """Close the response and release the connection. Automatically called if the response body is read to completion. """ self.http_response.close() def iter_bytes(self, chunk_size: int | None = None) -> Iterator[bytes]: """ A byte-iterator over the decoded response content. This automatically handles gzip, deflate and brotli encoded responses. """ for chunk in self.http_response.iter_bytes(chunk_size): yield chunk def iter_text(self, chunk_size: int | None = None) -> Iterator[str]: """A str-iterator over the decoded response content that handles both gzip, deflate, etc but also detects the content's string encoding. """ for chunk in self.http_response.iter_text(chunk_size): yield chunk def iter_lines(self) -> Iterator[str]: """Like `iter_text()` but will only yield chunks for each line""" for chunk in self.http_response.iter_lines(): yield chunk class AsyncAPIResponse(BaseAPIResponse[R]): @property def request_id(self) -> str | None: return self.http_response.headers.get("request-id") # type: ignore[no-any-return] @overload async def parse(self, *, to: type[_T]) -> _T: ... @overload async def parse(self) -> R: ... async def parse(self, *, to: type[_T] | None = None) -> R | _T: """Returns the rich python representation of this response's data. For lower-level control, see `.read()`, `.json()`, `.iter_bytes()`. You can customise the type that the response is parsed into through the `to` argument, e.g. ```py from anthropic import BaseModel class MyModel(BaseModel): foo: str obj = response.parse(to=MyModel) print(obj.foo) ``` We support parsing: - `BaseModel` - `dict` - `list` - `Union` - `str` - `httpx.Response` """ cache_key = to if to is not None else self._cast_to cached = self._parsed_by_type.get(cache_key) if cached is not None: return cached # type: ignore[no-any-return] if not self._is_sse_stream: await self.read() parsed = self._parse(to=to) if is_given(self._options.post_parser): parsed = self._options.post_parser(parsed) if isinstance(parsed, BaseModel): add_request_id(parsed, self.request_id) self._parsed_by_type[cache_key] = parsed return cast(R, parsed) async def read(self) -> bytes: """Read and return the binary response content.""" try: return await self.http_response.aread() except httpx.StreamConsumed as exc: # the default error raised by httpx isn't very # helpful in our case so we re-raise it with # a different error message raise StreamAlreadyConsumed() from exc async def text(self) -> str: """Read and decode the response content into a string.""" await self.read() return self.http_response.text async def json(self) -> object: """Read and decode the JSON response content.""" await self.read() return self.http_response.json() async def close(self) -> None: """Close the response and release the connection. Automatically called if the response body is read to completion. """ await self.http_response.aclose() async def iter_bytes(self, chunk_size: int | None = None) -> AsyncIterator[bytes]: """ A byte-iterator over the decoded response content. This automatically handles gzip, deflate and brotli encoded responses. """ async for chunk in self.http_response.aiter_bytes(chunk_size): yield chunk async def iter_text(self, chunk_size: int | None = None) -> AsyncIterator[str]: """A str-iterator over the decoded response content that handles both gzip, deflate, etc but also detects the content's string encoding. """ async for chunk in self.http_response.aiter_text(chunk_size): yield chunk async def iter_lines(self) -> AsyncIterator[str]: """Like `iter_text()` but will only yield chunks for each line""" async for chunk in self.http_response.aiter_lines(): yield chunk class BinaryAPIResponse(APIResponse[bytes]): """Subclass of APIResponse providing helpers for dealing with binary data. Note: If you want to stream the response data instead of eagerly reading it all at once then you should use `.with_streaming_response` when making the API request, e.g. `.with_streaming_response.get_binary_response()` """ def write_to_file( self, file: str | os.PathLike[str], ) -> None: """Write the output to the given file. Accepts a filename or any path-like object, e.g. pathlib.Path Note: if you want to stream the data to the file instead of writing all at once then you should use `.with_streaming_response` when making the API request, e.g. `.with_streaming_response.get_binary_response()` """ with open(file, mode="wb") as f: for data in self.iter_bytes(): f.write(data) class AsyncBinaryAPIResponse(AsyncAPIResponse[bytes]): """Subclass of APIResponse providing helpers for dealing with binary data. Note: If you want to stream the response data instead of eagerly reading it all at once then you should use `.with_streaming_response` when making the API request, e.g. `.with_streaming_response.get_binary_response()` """ async def write_to_file( self, file: str | os.PathLike[str], ) -> None: """Write the output to the given file. Accepts a filename or any path-like object, e.g. pathlib.Path Note: if you want to stream the data to the file instead of writing all at once then you should use `.with_streaming_response` when making the API request, e.g. `.with_streaming_response.get_binary_response()` """ path = anyio.Path(file) async with await path.open(mode="wb") as f: async for data in self.iter_bytes(): await f.write(data) class StreamedBinaryAPIResponse(APIResponse[bytes]): def stream_to_file( self, file: str | os.PathLike[str], *, chunk_size: int | None = None, ) -> None: """Streams the output to the given file. Accepts a filename or any path-like object, e.g. pathlib.Path """ with open(file, mode="wb") as f: for data in self.iter_bytes(chunk_size): f.write(data) class AsyncStreamedBinaryAPIResponse(AsyncAPIResponse[bytes]): async def stream_to_file( self, file: str | os.PathLike[str], *, chunk_size: int | None = None, ) -> None: """Streams the output to the given file. Accepts a filename or any path-like object, e.g. pathlib.Path """ path = anyio.Path(file) async with await path.open(mode="wb") as f: async for data in self.iter_bytes(chunk_size): await f.write(data) class MissingStreamClassError(TypeError): def __init__(self) -> None: super().__init__( "The `stream` argument was set to `True` but the `stream_cls` argument was not given. See `anthropic._streaming` for reference", ) class StreamAlreadyConsumed(AnthropicError): """ Attempted to read or stream content, but the content has already been streamed. This can happen if you use a method like `.iter_lines()` and then attempt to read th entire response body afterwards, e.g. ```py response = await client.post(...) async for line in response.iter_lines(): ... # do something with `line` content = await response.read() # ^ error ``` If you want this behaviour you'll need to either manually accumulate the response content or call `await response.read()` before iterating over the stream. """ def __init__(self) -> None: message = ( "Attempted to read or stream some content, but the content has " "already been streamed. " "This could be due to attempting to stream the response " "content more than once." "\n\n" "You can fix this by manually accumulating the response content while streaming " "or by calling `.read()` before starting to stream." ) super().__init__(message) class ResponseContextManager(Generic[_APIResponseT]): """Context manager for ensuring that a request is not made until it is entered and that the response will always be closed when the context manager exits """ def __init__(self, request_func: Callable[[], _APIResponseT]) -> None: self._request_func = request_func self.__response: _APIResponseT | None = None def __enter__(self) -> _APIResponseT: self.__response = self._request_func() return self.__response def __exit__( self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None, ) -> None: if self.__response is not None: self.__response.close() class AsyncResponseContextManager(Generic[_AsyncAPIResponseT]): """Context manager for ensuring that a request is not made until it is entered and that the response will always be closed when the context manager exits """ def __init__(self, api_request: Awaitable[_AsyncAPIResponseT]) -> None: self._api_request = api_request self.__response: _AsyncAPIResponseT | None = None async def __aenter__(self) -> _AsyncAPIResponseT: self.__response = await self._api_request return self.__response async def __aexit__( self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None, ) -> None: if self.__response is not None: await self.__response.close() def to_streamed_response_wrapper(func: Callable[P, R]) -> Callable[P, ResponseContextManager[APIResponse[R]]]: """Higher order function that takes one of our bound API methods and wraps it to support streaming and returning the raw `APIResponse` object directly. """ @functools.wraps(func) def wrapped(*args: P.args, **kwargs: P.kwargs) -> ResponseContextManager[APIResponse[R]]: extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "stream" kwargs["extra_headers"] = extra_headers make_request = functools.partial(func, *args, **kwargs) return ResponseContextManager(cast(Callable[[], APIResponse[R]], make_request)) return wrapped def async_to_streamed_response_wrapper( func: Callable[P, Awaitable[R]], ) -> Callable[P, AsyncResponseContextManager[AsyncAPIResponse[R]]]: """Higher order function that takes one of our bound API methods and wraps it to support streaming and returning the raw `APIResponse` object directly. """ @functools.wraps(func) def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncResponseContextManager[AsyncAPIResponse[R]]: extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "stream" kwargs["extra_headers"] = extra_headers make_request = func(*args, **kwargs) return AsyncResponseContextManager(cast(Awaitable[AsyncAPIResponse[R]], make_request)) return wrapped def to_custom_streamed_response_wrapper( func: Callable[P, object], response_cls: type[_APIResponseT], ) -> Callable[P, ResponseContextManager[_APIResponseT]]: """Higher order function that takes one of our bound API methods and an `APIResponse` class and wraps the method to support streaming and returning the given response class directly. Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` """ @functools.wraps(func) def wrapped(*args: P.args, **kwargs: P.kwargs) -> ResponseContextManager[_APIResponseT]: extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "stream" extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls kwargs["extra_headers"] = extra_headers make_request = functools.partial(func, *args, **kwargs) return ResponseContextManager(cast(Callable[[], _APIResponseT], make_request)) return wrapped def async_to_custom_streamed_response_wrapper( func: Callable[P, Awaitable[object]], response_cls: type[_AsyncAPIResponseT], ) -> Callable[P, AsyncResponseContextManager[_AsyncAPIResponseT]]: """Higher order function that takes one of our bound API methods and an `APIResponse` class and wraps the method to support streaming and returning the given response class directly. Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` """ @functools.wraps(func) def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncResponseContextManager[_AsyncAPIResponseT]: extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "stream" extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls kwargs["extra_headers"] = extra_headers make_request = func(*args, **kwargs) return AsyncResponseContextManager(cast(Awaitable[_AsyncAPIResponseT], make_request)) return wrapped def to_raw_response_wrapper(func: Callable[P, R]) -> Callable[P, APIResponse[R]]: """Higher order function that takes one of our bound API methods and wraps it to support returning the raw `APIResponse` object directly. """ @functools.wraps(func) def wrapped(*args: P.args, **kwargs: P.kwargs) -> APIResponse[R]: extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "raw" kwargs["extra_headers"] = extra_headers return cast(APIResponse[R], func(*args, **kwargs)) return wrapped def async_to_raw_response_wrapper(func: Callable[P, Awaitable[R]]) -> Callable[P, Awaitable[AsyncAPIResponse[R]]]: """Higher order function that takes one of our bound API methods and wraps it to support returning the raw `APIResponse` object directly. """ @functools.wraps(func) async def wrapped(*args: P.args, **kwargs: P.kwargs) -> AsyncAPIResponse[R]: extra_headers: dict[str, str] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "raw" kwargs["extra_headers"] = extra_headers return cast(AsyncAPIResponse[R], await func(*args, **kwargs)) return wrapped def to_custom_raw_response_wrapper( func: Callable[P, object], response_cls: type[_APIResponseT], ) -> Callable[P, _APIResponseT]: """Higher order function that takes one of our bound API methods and an `APIResponse` class and wraps the method to support returning the given response class directly. Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` """ @functools.wraps(func) def wrapped(*args: P.args, **kwargs: P.kwargs) -> _APIResponseT: extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "raw" extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls kwargs["extra_headers"] = extra_headers return cast(_APIResponseT, func(*args, **kwargs)) return wrapped def async_to_custom_raw_response_wrapper( func: Callable[P, Awaitable[object]], response_cls: type[_AsyncAPIResponseT], ) -> Callable[P, Awaitable[_AsyncAPIResponseT]]: """Higher order function that takes one of our bound API methods and an `APIResponse` class and wraps the method to support returning the given response class directly. Note: the given `response_cls` *must* be concrete, e.g. `class BinaryAPIResponse(APIResponse[bytes])` """ @functools.wraps(func) def wrapped(*args: P.args, **kwargs: P.kwargs) -> Awaitable[_AsyncAPIResponseT]: extra_headers: dict[str, Any] = {**(cast(Any, kwargs.get("extra_headers")) or {})} extra_headers[RAW_RESPONSE_HEADER] = "raw" extra_headers[OVERRIDE_CAST_TO_HEADER] = response_cls kwargs["extra_headers"] = extra_headers return cast(Awaitable[_AsyncAPIResponseT], func(*args, **kwargs)) return wrapped def extract_response_type(typ: type[BaseAPIResponse[Any]]) -> type: """Given a type like `APIResponse[T]`, returns the generic type variable `T`. This also handles the case where a concrete subclass is given, e.g. ```py class MyResponse(APIResponse[bytes]): ... extract_response_type(MyResponse) -> bytes ``` """ return extract_type_var_from_base( typ, generic_bases=cast("tuple[type, ...]", (BaseAPIResponse, APIResponse, AsyncAPIResponse)), index=0, ) anthropic-sdk-python-0.120.2/src/anthropic/_streaming.py000066400000000000000000000505751523216435200232400ustar00rootroot00000000000000# Note: initially copied from https://github.com/florimondmanca/httpx-sse/blob/master/src/httpx_sse/_decoders.py from __future__ import annotations import abc import json import inspect import warnings from types import TracebackType from typing import TYPE_CHECKING, Any, Generic, TypeVar, Iterator, Optional, AsyncIterator, cast from typing_extensions import Self, Protocol, TypeGuard, override, get_origin, runtime_checkable import httpx from ._utils import is_dict, extract_type_var_from_base if TYPE_CHECKING: from ._client import Anthropic, AsyncAnthropic from ._models import FinalRequestOptions _T = TypeVar("_T") class _SyncStreamMeta(abc.ABCMeta): @override def __instancecheck__(self, instance: Any) -> bool: # we override the `isinstance()` check for `Stream` # as a previous version of the `MessageStream` class # inherited from `Stream` & without this workaround, # changing it to not inherit would be a breaking change. from .lib.streaming import MessageStream if isinstance(instance, MessageStream): warnings.warn( "Using `isinstance()` to check if a `MessageStream` object is an instance of `Stream` is deprecated & will be removed in the next major version", DeprecationWarning, stacklevel=2, ) return True return False class Stream(Generic[_T], metaclass=_SyncStreamMeta): """Provides the core interface to iterate over a synchronous stream response.""" response: httpx.Response _options: Optional[FinalRequestOptions] = None _decoder: SSEBytesDecoder def __init__( self, *, cast_to: type[_T], response: httpx.Response, client: Anthropic, options: Optional[FinalRequestOptions] = None, ) -> None: self.response = response self._cast_to = cast_to self._client = client self._options = options self._decoder = client._make_sse_decoder() self._iterator = self.__stream__() def __next__(self) -> _T: return self._iterator.__next__() def __iter__(self) -> Iterator[_T]: for item in self._iterator: yield item def _iter_events(self) -> Iterator[ServerSentEvent]: yield from self._decoder.iter_bytes(self.response.iter_bytes()) @staticmethod def raw_events(response: httpx.Response) -> Iterator[ServerSentEvent]: """Iterate the raw Server-Sent Events from `response`, before any JSON parsing or event-name filtering. This reads the response body directly, so the response is consumed. """ return SSEDecoder().iter_bytes(response.iter_bytes()) def __stream__(self) -> Iterator[_T]: cast_to = cast(Any, self._cast_to) response = self.response process_data = self._client._process_response_data iterator = self._iter_events() try: for sse in iterator: if sse.event == "completion": yield process_data(data=sse.json(), cast_to=cast_to, response=response) if ( sse.event == "message_start" or sse.event == "message_delta" or sse.event == "message_stop" or sse.event == "content_block_start" or sse.event == "content_block_delta" or sse.event == "content_block_stop" or sse.event == "message" or sse.event == "user.message" or sse.event == "user.interrupt" or sse.event == "user.tool_confirmation" or sse.event == "user.custom_tool_result" or sse.event == "user.tool_result" or sse.event == "agent.message" or sse.event == "agent.thinking" or sse.event == "agent.tool_use" or sse.event == "agent.tool_result" or sse.event == "agent.mcp_tool_use" or sse.event == "agent.mcp_tool_result" or sse.event == "agent.custom_tool_use" or sse.event == "agent.thread_context_compacted" or sse.event == "session.status_running" or sse.event == "session.status_idle" or sse.event == "session.status_rescheduled" or sse.event == "session.status_terminated" or sse.event == "session.error" or sse.event == "session.deleted" or sse.event == "session.updated" or sse.event == "span.model_request_start" or sse.event == "span.model_request_end" or sse.event == "span.outcome_evaluation_start" or sse.event == "span.outcome_evaluation_ongoing" or sse.event == "span.outcome_evaluation_end" or sse.event == "user.define_outcome" or sse.event == "agent.thread_message_received" or sse.event == "agent.thread_message_sent" or sse.event == "agent.session_thread_message_received" or sse.event == "agent.session_thread_message_sent" or sse.event == "session.thread_created" or sse.event == "session.thread_status_created" or sse.event == "session.thread_status_running" or sse.event == "session.thread_status_idle" or sse.event == "session.thread_status_rescheduled" or sse.event == "session.thread_status_terminated" or sse.event == "event_start" or sse.event == "event_delta" or sse.event == "system.message" ): data = sse.json() if is_dict(data) and "type" not in data: data["type"] = sse.event yield process_data(data=data, cast_to=cast_to, response=response) if sse.event == "ping": continue if sse.event == "error": body = sse.data try: body = sse.json() err_msg = f"{body}" except Exception: err_msg = sse.data or f"Error code: {response.status_code}" raise self._client._make_status_error( err_msg, body=body, response=self.response, ) finally: # Ensure the response is closed even if the consumer doesn't read all data response.close() def __enter__(self) -> Self: return self def __exit__( self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None, ) -> None: self.close() def close(self) -> None: """ Close the response and release the connection. Automatically called if the response body is read to completion. """ self.response.close() class _AsyncStreamMeta(abc.ABCMeta): @override def __instancecheck__(self, instance: Any) -> bool: # we override the `isinstance()` check for `AsyncStream` # as a previous version of the `AsyncMessageStream` class # inherited from `AsyncStream` & without this workaround, # changing it to not inherit would be a breaking change. from .lib.streaming import AsyncMessageStream if isinstance(instance, AsyncMessageStream): warnings.warn( "Using `isinstance()` to check if a `AsyncMessageStream` object is an instance of `AsyncStream` is deprecated & will be removed in the next major version", DeprecationWarning, stacklevel=2, ) return True return False class AsyncStream(Generic[_T], metaclass=_AsyncStreamMeta): """Provides the core interface to iterate over an asynchronous stream response.""" response: httpx.Response _options: Optional[FinalRequestOptions] = None _decoder: SSEDecoder | SSEBytesDecoder def __init__( self, *, cast_to: type[_T], response: httpx.Response, client: AsyncAnthropic, options: Optional[FinalRequestOptions] = None, ) -> None: self.response = response self._cast_to = cast_to self._client = client self._options = options self._decoder = client._make_sse_decoder() self._iterator = self.__stream__() async def __anext__(self) -> _T: return await self._iterator.__anext__() async def __aiter__(self) -> AsyncIterator[_T]: async for item in self._iterator: yield item async def _iter_events(self) -> AsyncIterator[ServerSentEvent]: async for sse in self._decoder.aiter_bytes(self.response.aiter_bytes()): yield sse @staticmethod def raw_events(response: httpx.Response) -> AsyncIterator[ServerSentEvent]: """Iterate the raw Server-Sent Events from `response`, before any JSON parsing or event-name filtering. This reads the response body directly, so the response is consumed. """ return SSEDecoder().aiter_bytes(response.aiter_bytes()) async def __stream__(self) -> AsyncIterator[_T]: cast_to = cast(Any, self._cast_to) response = self.response process_data = self._client._process_response_data iterator = self._iter_events() try: async for sse in iterator: if sse.event == "completion": yield process_data(data=sse.json(), cast_to=cast_to, response=response) if ( sse.event == "message_start" or sse.event == "message_delta" or sse.event == "message_stop" or sse.event == "content_block_start" or sse.event == "content_block_delta" or sse.event == "content_block_stop" or sse.event == "message" or sse.event == "user.message" or sse.event == "user.interrupt" or sse.event == "user.tool_confirmation" or sse.event == "user.custom_tool_result" or sse.event == "user.tool_result" or sse.event == "agent.message" or sse.event == "agent.thinking" or sse.event == "agent.tool_use" or sse.event == "agent.tool_result" or sse.event == "agent.mcp_tool_use" or sse.event == "agent.mcp_tool_result" or sse.event == "agent.custom_tool_use" or sse.event == "agent.thread_context_compacted" or sse.event == "session.status_running" or sse.event == "session.status_idle" or sse.event == "session.status_rescheduled" or sse.event == "session.status_terminated" or sse.event == "session.error" or sse.event == "session.deleted" or sse.event == "session.updated" or sse.event == "span.model_request_start" or sse.event == "span.model_request_end" or sse.event == "span.outcome_evaluation_start" or sse.event == "span.outcome_evaluation_ongoing" or sse.event == "span.outcome_evaluation_end" or sse.event == "user.define_outcome" or sse.event == "agent.thread_message_received" or sse.event == "agent.thread_message_sent" or sse.event == "agent.session_thread_message_received" or sse.event == "agent.session_thread_message_sent" or sse.event == "session.thread_created" or sse.event == "session.thread_status_created" or sse.event == "session.thread_status_running" or sse.event == "session.thread_status_idle" or sse.event == "session.thread_status_rescheduled" or sse.event == "session.thread_status_terminated" or sse.event == "event_start" or sse.event == "event_delta" or sse.event == "system.message" ): data = sse.json() if is_dict(data) and "type" not in data: data["type"] = sse.event yield process_data(data=data, cast_to=cast_to, response=response) if sse.event == "ping": continue if sse.event == "error": body = sse.data try: body = sse.json() err_msg = f"{body}" except Exception: err_msg = sse.data or f"Error code: {response.status_code}" raise self._client._make_status_error( err_msg, body=body, response=self.response, ) finally: # Ensure the response is closed even if the consumer doesn't read all data await response.aclose() async def __aenter__(self) -> Self: return self async def __aexit__( self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None, ) -> None: await self.close() async def close(self) -> None: """ Close the response and release the connection. Automatically called if the response body is read to completion. """ await self.response.aclose() class ServerSentEvent: def __init__( self, *, event: str | None = None, data: str | None = None, id: str | None = None, retry: int | None = None, raw: list[str] | None = None, ) -> None: if data is None: data = "" self._id = id self._data = data self._event = event or None self._retry = retry self._raw = raw if raw is not None else [] @property def event(self) -> str | None: return self._event @property def id(self) -> str | None: return self._id @property def retry(self) -> int | None: return self._retry @property def data(self) -> str: return self._data @property def raw(self) -> list[str]: """The original wire lines this event was decoded from, without trailing newlines. Includes SSE fields the decoder does not otherwise model (comment lines, unknown fields). Empty for events that were constructed rather than decoded. """ return self._raw def json(self) -> Any: return json.loads(self.data) @override def __repr__(self) -> str: return f"ServerSentEvent(event={self.event}, data={self.data}, id={self.id}, retry={self.retry})" class SSEDecoder: _data: list[str] _event: str | None _retry: int | None _last_event_id: str | None _raw: list[str] def __init__(self) -> None: self._event = None self._data = [] self._last_event_id = None self._retry = None self._raw = [] def iter_bytes(self, iterator: Iterator[bytes]) -> Iterator[ServerSentEvent]: """Given an iterator that yields raw binary data, iterate over it & yield every event encountered""" for chunk in self._iter_chunks(iterator): # Split before decoding so splitlines() only uses \r and \n for raw_line in chunk.splitlines(): line = raw_line.decode("utf-8") sse = self.decode(line) if sse: yield sse def _iter_chunks(self, iterator: Iterator[bytes]) -> Iterator[bytes]: """Given an iterator that yields raw binary data, iterate over it and yield individual SSE chunks""" data = b"" for chunk in iterator: for line in chunk.splitlines(keepends=True): data += line if data.endswith((b"\r\r", b"\n\n", b"\r\n\r\n")): yield data data = b"" if data: yield data async def aiter_bytes(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[ServerSentEvent]: """Given an iterator that yields raw binary data, iterate over it & yield every event encountered""" async for chunk in self._aiter_chunks(iterator): # Split before decoding so splitlines() only uses \r and \n for raw_line in chunk.splitlines(): line = raw_line.decode("utf-8") sse = self.decode(line) if sse: yield sse async def _aiter_chunks(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[bytes]: """Given an iterator that yields raw binary data, iterate over it and yield individual SSE chunks""" data = b"" async for chunk in iterator: for line in chunk.splitlines(keepends=True): data += line if data.endswith((b"\r\r", b"\n\n", b"\r\n\r\n")): yield data data = b"" if data: yield data def decode(self, line: str) -> ServerSentEvent | None: # See: https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation # noqa: E501 if not line: if not self._event and not self._data and not self._last_event_id and self._retry is None: self._raw = [] return None sse = ServerSentEvent( event=self._event, data="\n".join(self._data), id=self._last_event_id, retry=self._retry, raw=self._raw, ) # NOTE: as per the SSE spec, do not reset last_event_id. self._event = None self._data = [] self._retry = None self._raw = [] return sse self._raw.append(line) if line.startswith(":"): return None fieldname, _, value = line.partition(":") if value.startswith(" "): value = value[1:] if fieldname == "event": self._event = value elif fieldname == "data": self._data.append(value) elif fieldname == "id": if "\0" in value: pass else: self._last_event_id = value elif fieldname == "retry": try: self._retry = int(value) except (TypeError, ValueError): pass else: pass # Field is ignored. return None @runtime_checkable class SSEBytesDecoder(Protocol): def iter_bytes(self, iterator: Iterator[bytes]) -> Iterator[ServerSentEvent]: """Given an iterator that yields raw binary data, iterate over it & yield every event encountered""" ... def aiter_bytes(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[ServerSentEvent]: """Given an async iterator that yields raw binary data, iterate over it & yield every event encountered""" ... def is_stream_class_type(typ: type) -> TypeGuard[type[Stream[object]] | type[AsyncStream[object]]]: """TypeGuard for determining whether or not the given type is a subclass of `Stream` / `AsyncStream`""" origin = get_origin(typ) or typ return inspect.isclass(origin) and issubclass(origin, (Stream, AsyncStream)) def extract_stream_chunk_type( stream_cls: type, *, failure_message: str | None = None, ) -> type: """Given a type like `Stream[T]`, returns the generic type variable `T`. This also handles the case where a concrete subclass is given, e.g. ```py class MyStream(Stream[bytes]): ... extract_stream_chunk_type(MyStream) -> bytes ``` """ from ._base_client import Stream, AsyncStream return extract_type_var_from_base( stream_cls, index=0, generic_bases=cast("tuple[type, ...]", (Stream, AsyncStream)), failure_message=failure_message, ) anthropic-sdk-python-0.120.2/src/anthropic/_types.py000066400000000000000000000171741523216435200224110ustar00rootroot00000000000000from __future__ import annotations from os import PathLike from typing import ( IO, TYPE_CHECKING, Any, Dict, List, Type, Tuple, Union, Mapping, TypeVar, Callable, Iterable, Iterator, Optional, Sequence, AsyncIterable, ) from typing_extensions import ( Set, Literal, Protocol, TypeAlias, TypedDict, SupportsIndex, overload, override, runtime_checkable, ) import httpx import pydantic from httpx import URL, Proxy, Timeout, Response, BaseTransport, AsyncBaseTransport if TYPE_CHECKING: from ._models import BaseModel from ._response import APIResponse, AsyncAPIResponse from ._legacy_response import HttpxBinaryResponseContent Transport = BaseTransport AsyncTransport = AsyncBaseTransport Query = Mapping[str, object] Body = object AnyMapping = Mapping[str, object] ModelT = TypeVar("ModelT", bound=pydantic.BaseModel) _T = TypeVar("_T") ArrayFormat = Literal["comma", "repeat", "indices", "brackets"] NestedFormat = Literal["dots", "brackets"] # Approximates httpx internal ProxiesTypes and RequestFiles types # while adding support for `PathLike` instances ProxiesDict = Dict["str | URL", Union[None, str, URL, Proxy]] ProxiesTypes = Union[str, Proxy, ProxiesDict] if TYPE_CHECKING: Base64FileInput = Union[IO[bytes], PathLike[str]] FileContent = Union[IO[bytes], bytes, PathLike[str]] else: Base64FileInput = Union[IO[bytes], PathLike] FileContent = Union[IO[bytes], bytes, PathLike] # PathLike is not subscriptable in Python 3.8. # Used for sending raw binary data / streaming data in request bodies # e.g. for file uploads without multipart encoding BinaryTypes = Union[bytes, bytearray, IO[bytes], Iterable[bytes]] AsyncBinaryTypes = Union[bytes, bytearray, IO[bytes], AsyncIterable[bytes]] FileTypes = Union[ # file (or bytes) FileContent, # (filename, file (or bytes)) Tuple[Optional[str], FileContent], # (filename, file (or bytes), content_type) Tuple[Optional[str], FileContent, Optional[str]], # (filename, file (or bytes), content_type, headers) Tuple[Optional[str], FileContent, Optional[str], Mapping[str, str]], ] RequestFiles = Union[Mapping[str, FileTypes], Sequence[Tuple[str, FileTypes]]] # duplicate of the above but without our custom file support HttpxFileContent = Union[IO[bytes], bytes] HttpxFileTypes = Union[ # file (or bytes) HttpxFileContent, # (filename, file (or bytes)) Tuple[Optional[str], HttpxFileContent], # (filename, file (or bytes), content_type) Tuple[Optional[str], HttpxFileContent, Optional[str]], # (filename, file (or bytes), content_type, headers) Tuple[Optional[str], HttpxFileContent, Optional[str], Mapping[str, str]], ] HttpxRequestFiles = Union[Mapping[str, HttpxFileTypes], Sequence[Tuple[str, HttpxFileTypes]]] # Workaround to support (cast_to: Type[ResponseT]) -> ResponseT # where ResponseT includes `None`. In order to support directly # passing `None`, overloads would have to be defined for every # method that uses `ResponseT` which would lead to an unacceptable # amount of code duplication and make it unreadable. See _base_client.py # for example usage. # # This unfortunately means that you will either have # to import this type and pass it explicitly: # # from anthropic import NoneType # client.get('/foo', cast_to=NoneType) # # or build it yourself: # # client.get('/foo', cast_to=type(None)) if TYPE_CHECKING: NoneType: Type[None] else: NoneType = type(None) class RequestOptions(TypedDict, total=False): headers: Headers max_retries: int timeout: float | Timeout | None params: Query extra_json: AnyMapping idempotency_key: str follow_redirects: bool # Sentinel class used until PEP 0661 is accepted class NotGiven: """ For parameters with a meaningful None value, we need to distinguish between the user explicitly passing None, and the user not passing the parameter at all. User code shouldn't need to use not_given directly. For example: ```py def create(timeout: Timeout | None | NotGiven = not_given): ... create(timeout=1) # 1s timeout create(timeout=None) # No timeout create() # Default timeout behavior ``` """ def __bool__(self) -> Literal[False]: return False @override def __repr__(self) -> str: return "NOT_GIVEN" not_given = NotGiven() # for backwards compatibility: NOT_GIVEN = NotGiven() class Omit: """ To explicitly omit something from being sent in a request, use `omit`. ```py # as the default `Content-Type` header is `application/json` that will be sent client.post("/upload/files", files={"file": b"my raw file content"}) # you can't explicitly override the header as it has to be dynamically generated # to look something like: 'multipart/form-data; boundary=0d8382fcf5f8c3be01ca2e11002d2983' client.post(..., headers={"Content-Type": "multipart/form-data"}) # instead you can remove the default `application/json` header by passing omit client.post(..., headers={"Content-Type": omit}) ``` """ def __bool__(self) -> Literal[False]: return False omit = Omit() @runtime_checkable class ModelBuilderProtocol(Protocol): @classmethod def build( cls: type[_T], *, response: Response, data: object, ) -> _T: ... Headers = Mapping[str, Union[str, Omit]] class HeadersLikeProtocol(Protocol): def get(self, __key: str) -> str | None: ... HeadersLike = Union[Headers, HeadersLikeProtocol] ResponseT = TypeVar( "ResponseT", bound=Union[ object, str, None, "BaseModel", List[Any], Dict[str, Any], Response, ModelBuilderProtocol, "APIResponse[Any]", "AsyncAPIResponse[Any]", "HttpxBinaryResponseContent", ], ) StrBytesIntFloat = Union[str, bytes, int, float] # Note: copied from Pydantic # https://github.com/pydantic/pydantic/blob/6f31f8f68ef011f84357330186f603ff295312fd/pydantic/main.py#L79 IncEx: TypeAlias = Union[Set[int], Set[str], Mapping[int, Union["IncEx", bool]], Mapping[str, Union["IncEx", bool]]] PostParser = Callable[[Any], Any] @runtime_checkable class InheritsGeneric(Protocol): """Represents a type that has inherited from `Generic` The `__orig_bases__` property can be used to determine the resolved type variable for a given base class. """ __orig_bases__: tuple[_GenericAlias] class _GenericAlias(Protocol): __origin__: type[object] class HttpxSendArgs(TypedDict, total=False): auth: httpx.Auth follow_redirects: bool _T_co = TypeVar("_T_co", covariant=True) if TYPE_CHECKING: # This works because str.__contains__ does not accept object (either in typeshed or at runtime) # https://github.com/hauntsaninja/useful_types/blob/5e9710f3875107d068e7679fd7fec9cfab0eff3b/useful_types/__init__.py#L285 # # Note: index() and count() methods are intentionally omitted to allow pyright to properly # infer TypedDict types when dict literals are used in lists assigned to SequenceNotStr. class SequenceNotStr(Protocol[_T_co]): @overload def __getitem__(self, index: SupportsIndex, /) -> _T_co: ... @overload def __getitem__(self, index: slice, /) -> Sequence[_T_co]: ... def __contains__(self, value: object, /) -> bool: ... def __len__(self) -> int: ... def __iter__(self) -> Iterator[_T_co]: ... def __reversed__(self) -> Iterator[_T_co]: ... else: # just point this to a normal `Sequence` at runtime to avoid having to special case # deserializing our custom sequence type SequenceNotStr = Sequence anthropic-sdk-python-0.120.2/src/anthropic/_utils/000077500000000000000000000000001523216435200220215ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/_utils/__init__.py000066400000000000000000000045031523216435200241340ustar00rootroot00000000000000from ._path import path_template as path_template from ._sync import asyncify as asyncify from ._proxy import LazyProxy as LazyProxy from ._utils import ( flatten as flatten, is_dict as is_dict, is_list as is_list, is_given as is_given, is_tuple as is_tuple, json_safe as json_safe, lru_cache as lru_cache, is_mapping as is_mapping, is_tuple_t as is_tuple_t, is_iterable as is_iterable, is_sequence as is_sequence, coerce_float as coerce_float, is_mapping_t as is_mapping_t, removeprefix as removeprefix, removesuffix as removesuffix, extract_files as extract_files, is_sequence_t as is_sequence_t, required_args as required_args, coerce_boolean as coerce_boolean, coerce_integer as coerce_integer, file_from_path as file_from_path, strip_not_given as strip_not_given, get_async_library as get_async_library, maybe_coerce_float as maybe_coerce_float, get_required_header as get_required_header, maybe_coerce_boolean as maybe_coerce_boolean, maybe_coerce_integer as maybe_coerce_integer, ) from ._compat import ( get_args as get_args, is_union as is_union, get_origin as get_origin, is_typeddict as is_typeddict, is_literal_type as is_literal_type, ) from ._typing import ( is_list_type as is_list_type, is_union_type as is_union_type, extract_type_arg as extract_type_arg, is_iterable_type as is_iterable_type, is_required_type as is_required_type, is_sequence_type as is_sequence_type, is_annotated_type as is_annotated_type, is_type_alias_type as is_type_alias_type, strip_annotated_type as strip_annotated_type, extract_type_var_from_base as extract_type_var_from_base, ) from ._streams import consume_sync_iterator as consume_sync_iterator, consume_async_iterator as consume_async_iterator from ._transform import ( PropertyInfo as PropertyInfo, transform as transform, async_transform as async_transform, maybe_transform as maybe_transform, async_maybe_transform as async_maybe_transform, ) from ._reflection import ( function_has_argument as function_has_argument, assert_overloads_in_sync as assert_overloads_in_sync, assert_signatures_in_sync as assert_signatures_in_sync, ) from ._datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime anthropic-sdk-python-0.120.2/src/anthropic/_utils/_compat.py000066400000000000000000000023171523216435200240200ustar00rootroot00000000000000from __future__ import annotations import sys import typing_extensions from typing import Any, Type, Union, Literal, Optional from datetime import date, datetime from typing_extensions import get_args as _get_args, get_origin as _get_origin from .._types import StrBytesIntFloat from ._datetime_parse import parse_date as _parse_date, parse_datetime as _parse_datetime _LITERAL_TYPES = {Literal, typing_extensions.Literal} def get_args(tp: type[Any]) -> tuple[Any, ...]: return _get_args(tp) def get_origin(tp: type[Any]) -> type[Any] | None: return _get_origin(tp) def is_union(tp: Optional[Type[Any]]) -> bool: if sys.version_info < (3, 10): return tp is Union # type: ignore[comparison-overlap] else: import types return tp is Union or tp is types.UnionType # type: ignore[comparison-overlap] def is_typeddict(tp: Type[Any]) -> bool: return typing_extensions.is_typeddict(tp) def is_literal_type(tp: Type[Any]) -> bool: return get_origin(tp) in _LITERAL_TYPES def parse_date(value: Union[date, StrBytesIntFloat]) -> date: return _parse_date(value) def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: return _parse_datetime(value) anthropic-sdk-python-0.120.2/src/anthropic/_utils/_datetime_parse.py000066400000000000000000000101541523216435200255210ustar00rootroot00000000000000""" This file contains code from https://github.com/pydantic/pydantic/blob/main/pydantic/v1/datetime_parse.py without the Pydantic v1 specific errors. """ from __future__ import annotations import re from typing import Dict, Union, Optional from datetime import date, datetime, timezone, timedelta from .._types import StrBytesIntFloat date_expr = r"(?P\d{4})-(?P\d{1,2})-(?P\d{1,2})" time_expr = ( r"(?P\d{1,2}):(?P\d{1,2})" r"(?::(?P\d{1,2})(?:\.(?P\d{1,6})\d{0,6})?)?" r"(?PZ|[+-]\d{2}(?::?\d{2})?)?$" ) date_re = re.compile(f"{date_expr}$") datetime_re = re.compile(f"{date_expr}[T ]{time_expr}") EPOCH = datetime(1970, 1, 1) # if greater than this, the number is in ms, if less than or equal it's in seconds # (in seconds this is 11th October 2603, in ms it's 20th August 1970) MS_WATERSHED = int(2e10) # slightly more than datetime.max in ns - (datetime.max - EPOCH).total_seconds() * 1e9 MAX_NUMBER = int(3e20) def _get_numeric(value: StrBytesIntFloat, native_expected_type: str) -> Union[None, int, float]: if isinstance(value, (int, float)): return value try: return float(value) except ValueError: return None except TypeError: raise TypeError(f"invalid type; expected {native_expected_type}, string, bytes, int or float") from None def _from_unix_seconds(seconds: Union[int, float]) -> datetime: if seconds > MAX_NUMBER: return datetime.max elif seconds < -MAX_NUMBER: return datetime.min while abs(seconds) > MS_WATERSHED: seconds /= 1000 dt = EPOCH + timedelta(seconds=seconds) return dt.replace(tzinfo=timezone.utc) def _parse_timezone(value: Optional[str]) -> Union[None, int, timezone]: if value == "Z": return timezone.utc elif value is not None: offset_mins = int(value[-2:]) if len(value) > 3 else 0 offset = 60 * int(value[1:3]) + offset_mins if value[0] == "-": offset = -offset return timezone(timedelta(minutes=offset)) else: return None def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime: """ Parse a datetime/int/float/string and return a datetime.datetime. This function supports time zone offsets. When the input contains one, the output uses a timezone with a fixed offset from UTC. Raise ValueError if the input is well formatted but not a valid datetime. Raise ValueError if the input isn't well formatted. """ if isinstance(value, datetime): return value number = _get_numeric(value, "datetime") if number is not None: return _from_unix_seconds(number) if isinstance(value, bytes): value = value.decode() assert not isinstance(value, (float, int)) match = datetime_re.match(value) if match is None: raise ValueError("invalid datetime format") kw = match.groupdict() if kw["microsecond"]: kw["microsecond"] = kw["microsecond"].ljust(6, "0") tzinfo = _parse_timezone(kw.pop("tzinfo")) kw_: Dict[str, Union[None, int, timezone]] = {k: int(v) for k, v in kw.items() if v is not None} kw_["tzinfo"] = tzinfo return datetime(**kw_) # type: ignore def parse_date(value: Union[date, StrBytesIntFloat]) -> date: """ Parse a date/int/float/string and return a datetime.date. Raise ValueError if the input is well formatted but not a valid date. Raise ValueError if the input isn't well formatted. """ if isinstance(value, date): if isinstance(value, datetime): return value.date() else: return value number = _get_numeric(value, "date") if number is not None: return _from_unix_seconds(number).date() if isinstance(value, bytes): value = value.decode() assert not isinstance(value, (float, int)) match = date_re.match(value) if match is None: raise ValueError("invalid date format") kw = {k: int(v) for k, v in match.groupdict().items()} try: return date(**kw) except ValueError: raise ValueError("invalid date format") from None anthropic-sdk-python-0.120.2/src/anthropic/_utils/_httpx.py000066400000000000000000000040561523216435200237060ustar00rootroot00000000000000""" This file includes code adapted from HTTPX's utility module (https://github.com/encode/httpx/blob/336204f0121a9aefdebac5cacd81f912bafe8057/httpx/_utils.py). We implement custom proxy handling to support configurations like `socket_options`, which are not currently configurable through the HTTPX client. For more context, see: https://github.com/encode/httpx/discussions/3514 """ from __future__ import annotations import ipaddress from typing import Mapping from urllib.request import getproxies def is_ipv4_hostname(hostname: str) -> bool: try: ipaddress.IPv4Address(hostname.split("/")[0]) except Exception: return False return True def is_ipv6_hostname(hostname: str) -> bool: try: ipaddress.IPv6Address(hostname.split("/")[0]) except Exception: return False return True def get_environment_proxies() -> Mapping[str, str | None]: """ Gets the proxy mappings based on environment variables. We use our own logic to parse these variables, as HTTPX doesn’t allow full configuration of the underlying transport when proxies are set via environment variables. """ proxy_info = getproxies() mounts: dict[str, str | None] = {} for scheme in ("http", "https", "all"): if proxy_info.get(scheme): hostname = proxy_info[scheme] mounts[f"{scheme}://"] = hostname if "://" in hostname else f"http://{hostname}" no_proxy_hosts = [host.strip() for host in proxy_info.get("no", "").split(",")] for hostname in no_proxy_hosts: if hostname == "*": return {} elif hostname: if "://" in hostname: mounts[hostname] = None elif is_ipv4_hostname(hostname): mounts[f"all://{hostname}"] = None elif is_ipv6_hostname(hostname): mounts[f"all://[{hostname}]"] = None elif hostname.lower() == "localhost": mounts[f"all://{hostname}"] = None else: mounts[f"all://*{hostname}"] = None return mounts anthropic-sdk-python-0.120.2/src/anthropic/_utils/_json.py000066400000000000000000000017021523216435200235030ustar00rootroot00000000000000import json from typing import Any from datetime import datetime from typing_extensions import override import pydantic from .._compat import model_dump def openapi_dumps(obj: Any) -> bytes: """ Serialize an object to UTF-8 encoded JSON bytes. Extends the standard json.dumps with support for additional types commonly used in the SDK, such as `datetime`, `pydantic.BaseModel`, etc. """ return json.dumps( obj, cls=_CustomEncoder, # Uses the same defaults as httpx's JSON serialization ensure_ascii=False, separators=(",", ":"), allow_nan=False, ).encode() class _CustomEncoder(json.JSONEncoder): @override def default(self, o: Any) -> Any: if isinstance(o, datetime): return o.isoformat() if isinstance(o, pydantic.BaseModel): return model_dump(o, exclude_unset=True, mode="json", by_alias=True) return super().default(o) anthropic-sdk-python-0.120.2/src/anthropic/_utils/_logs.py000066400000000000000000000014171523216435200235010ustar00rootroot00000000000000import os import logging logger: logging.Logger = logging.getLogger("anthropic") httpx_logger: logging.Logger = logging.getLogger("httpx") def _basic_config() -> None: # e.g. [2023-10-05 14:12:26 - anthropic._base_client:818 - DEBUG] HTTP Request: POST http://127.0.0.1:4010/foo/bar "200 OK" logging.basicConfig( format="[%(asctime)s - %(name)s:%(lineno)d - %(levelname)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S", ) def setup_logging() -> None: env = os.environ.get("ANTHROPIC_LOG") if env == "debug": _basic_config() logger.setLevel(logging.DEBUG) httpx_logger.setLevel(logging.DEBUG) elif env == "info": _basic_config() logger.setLevel(logging.INFO) httpx_logger.setLevel(logging.INFO) anthropic-sdk-python-0.120.2/src/anthropic/_utils/_path.py000066400000000000000000000111351523216435200234670ustar00rootroot00000000000000from __future__ import annotations import re from typing import ( Any, Mapping, Callable, ) from urllib.parse import quote # Matches '.' or '..' where each dot is either literal or percent-encoded (%2e / %2E). _DOT_SEGMENT_RE = re.compile(r"^(?:\.|%2[eE]){1,2}$") _PLACEHOLDER_RE = re.compile(r"\{(\w+)\}") def _quote_path_segment_part(value: str) -> str: """Percent-encode `value` for use in a URI path segment. Considers characters not in `pchar` set from RFC 3986 §3.3 to be unsafe. https://datatracker.ietf.org/doc/html/rfc3986#section-3.3 """ # quote() already treats unreserved characters (letters, digits, and -._~) # as safe, so we only need to add sub-delims, ':', and '@'. # Notably, unlike the default `safe` for quote(), / is unsafe and must be quoted. return quote(value, safe="!$&'()*+,;=:@") def _quote_query_part(value: str) -> str: """Percent-encode `value` for use in a URI query string. Considers &, = and characters not in `query` set from RFC 3986 §3.4 to be unsafe. https://datatracker.ietf.org/doc/html/rfc3986#section-3.4 """ return quote(value, safe="!$'()*+,;:@/?") def _quote_fragment_part(value: str) -> str: """Percent-encode `value` for use in a URI fragment. Considers characters not in `fragment` set from RFC 3986 §3.5 to be unsafe. https://datatracker.ietf.org/doc/html/rfc3986#section-3.5 """ return quote(value, safe="!$&'()*+,;=:@/?") def _interpolate( template: str, values: Mapping[str, Any], quoter: Callable[[str], str], ) -> str: """Replace {name} placeholders in `template`, quoting each value with `quoter`. Placeholder names are looked up in `values`. Raises: KeyError: If a placeholder is not found in `values`. """ # re.split with a capturing group returns alternating # [text, name, text, name, ..., text] elements. parts = _PLACEHOLDER_RE.split(template) for i in range(1, len(parts), 2): name = parts[i] if name not in values: raise KeyError(f"a value for placeholder {{{name}}} was not provided") val = values[name] if val is None: parts[i] = "null" elif isinstance(val, bool): parts[i] = "true" if val else "false" else: parts[i] = quoter(str(values[name])) return "".join(parts) def path_template(template: str, /, **kwargs: Any) -> str: """Interpolate {name} placeholders in `template` from keyword arguments. Args: template: The template string containing {name} placeholders. **kwargs: Keyword arguments to interpolate into the template. Returns: The template with placeholders interpolated and percent-encoded. Safe characters for percent-encoding are dependent on the URI component. Placeholders in path and fragment portions are percent-encoded where the `segment` and `fragment` sets from RFC 3986 respectively are considered safe. Placeholders in the query portion are percent-encoded where the `query` set from RFC 3986 §3.3 is considered safe except for = and & characters. Raises: KeyError: If a placeholder is not found in `kwargs`. ValueError: If resulting path contains /./ or /../ segments (including percent-encoded dot-segments). """ # Split the template into path, query, and fragment portions. fragment_template: str | None = None query_template: str | None = None rest = template if "#" in rest: rest, fragment_template = rest.split("#", 1) if "?" in rest: rest, query_template = rest.split("?", 1) path_template = rest # Interpolate each portion with the appropriate quoting rules. path_result = _interpolate(path_template, kwargs, _quote_path_segment_part) # Reject dot-segments (. and ..) in the final assembled path. The check # runs after interpolation so that adjacent placeholders or a mix of static # text and placeholders that together form a dot-segment are caught. # Also reject percent-encoded dot-segments to protect against incorrectly # implemented normalization in servers/proxies. for segment in path_result.split("/"): if _DOT_SEGMENT_RE.match(segment): raise ValueError(f"Constructed path {path_result!r} contains dot-segment {segment!r} which is not allowed") result = path_result if query_template is not None: result += "?" + _interpolate(query_template, kwargs, _quote_query_part) if fragment_template is not None: result += "#" + _interpolate(fragment_template, kwargs, _quote_fragment_part) return result anthropic-sdk-python-0.120.2/src/anthropic/_utils/_proxy.py000066400000000000000000000036671523216435200237270ustar00rootroot00000000000000from __future__ import annotations from abc import ABC, abstractmethod from typing import Generic, TypeVar, Iterable, cast from typing_extensions import override T = TypeVar("T") class LazyProxy(Generic[T], ABC): """Implements data methods to pretend that an instance is another instance. This includes forwarding attribute access and other methods. """ # Note: we have to special case proxies that themselves return proxies # to support using a proxy as a catch-all for any random access, e.g. `proxy.foo.bar.baz` def __getattr__(self, attr: str) -> object: proxied = self.__get_proxied__() if isinstance(proxied, LazyProxy): return proxied # pyright: ignore return getattr(proxied, attr) @override def __repr__(self) -> str: proxied = self.__get_proxied__() if isinstance(proxied, LazyProxy): return proxied.__class__.__name__ return repr(self.__get_proxied__()) @override def __str__(self) -> str: proxied = self.__get_proxied__() if isinstance(proxied, LazyProxy): return proxied.__class__.__name__ return str(proxied) @override def __dir__(self) -> Iterable[str]: proxied = self.__get_proxied__() if isinstance(proxied, LazyProxy): return [] return proxied.__dir__() @property # type: ignore @override def __class__(self) -> type: # pyright: ignore try: proxied = self.__get_proxied__() except Exception: return type(self) if issubclass(type(proxied), LazyProxy): return type(proxied) return proxied.__class__ def __get_proxied__(self) -> T: return self.__load__() def __as_proxied__(self) -> T: """Helper method that returns the current proxy, typed as the loaded object""" return cast(T, self) @abstractmethod def __load__(self) -> T: ... anthropic-sdk-python-0.120.2/src/anthropic/_utils/_reflection.py000066400000000000000000000053211523216435200246650ustar00rootroot00000000000000from __future__ import annotations import inspect import typing_extensions from typing import Any, Callable def function_has_argument(func: Callable[..., Any], arg_name: str) -> bool: """Returns whether or not the given function has a specific parameter""" sig = inspect.signature(func) return arg_name in sig.parameters def assert_signatures_in_sync( source_func: Callable[..., Any], check_func: Callable[..., Any], *, exclude_params: set[str] = set(), ) -> None: """Ensure that the signature of the second function matches the first.""" check_sig = inspect.signature(check_func) source_sig = inspect.signature(source_func) errors: list[str] = [] for name, source_param in source_sig.parameters.items(): if name in exclude_params: continue custom_param = check_sig.parameters.get(name) if not custom_param: errors.append(f"the `{name}` param is missing") continue if custom_param.annotation != source_param.annotation: errors.append( f"types for the `{name}` param are do not match; source={repr(source_param.annotation)} checking={repr(custom_param.annotation)}" ) continue if errors: raise AssertionError(f"{len(errors)} errors encountered when comparing signatures:\n\n" + "\n\n".join(errors)) def assert_overloads_in_sync( source_func: Callable[..., Any], overloaded_func: Callable[..., Any], *, exclude_params: set[str] = set(), ) -> None: """Ensure that every @overload of overloaded_func contains all params from source_func.""" source_sig = inspect.signature(source_func) overloads = typing_extensions.get_overloads(overloaded_func) if not overloads: raise AssertionError(f"No @overload definitions found for {overloaded_func!r}") errors: list[str] = [] for i, overload_fn in enumerate(overloads): overload_sig = inspect.signature(overload_fn) for name, source_param in source_sig.parameters.items(): if name in exclude_params: continue overload_param = overload_sig.parameters.get(name) if not overload_param: errors.append(f"overload {i}: `{name}` param is missing") continue if overload_param.annotation != source_param.annotation: errors.append( f"overload {i}: types for `{name}` do not match; source={repr(source_param.annotation)} overload={repr(overload_param.annotation)}" ) if errors: raise AssertionError( f"{len(errors)} errors encountered when comparing overload signatures:\n\n" + "\n\n".join(errors) ) anthropic-sdk-python-0.120.2/src/anthropic/_utils/_resources_proxy.py000066400000000000000000000011341523216435200260040ustar00rootroot00000000000000from __future__ import annotations from typing import Any from typing_extensions import override from ._proxy import LazyProxy class ResourcesProxy(LazyProxy[Any]): """A proxy for the `anthropic.resources` module. This is used so that we can lazily import `anthropic.resources` only when needed *and* so that users can just import `anthropic` and reference `anthropic.resources` """ @override def __load__(self) -> Any: import importlib mod = importlib.import_module("anthropic.resources") return mod resources = ResourcesProxy().__as_proxied__() anthropic-sdk-python-0.120.2/src/anthropic/_utils/_streams.py000066400000000000000000000004411523216435200242070ustar00rootroot00000000000000from typing import Any from typing_extensions import Iterator, AsyncIterator def consume_sync_iterator(iterator: Iterator[Any]) -> None: for _ in iterator: ... async def consume_async_iterator(iterator: AsyncIterator[Any]) -> None: async for _ in iterator: ... anthropic-sdk-python-0.120.2/src/anthropic/_utils/_sync.py000066400000000000000000000030651523216435200235120ustar00rootroot00000000000000from __future__ import annotations import asyncio import functools from typing import TypeVar, Callable, Awaitable from typing_extensions import ParamSpec import anyio import sniffio import anyio.to_thread T_Retval = TypeVar("T_Retval") T_ParamSpec = ParamSpec("T_ParamSpec") async def to_thread( func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs ) -> T_Retval: if sniffio.current_async_library() == "asyncio": return await asyncio.to_thread(func, *args, **kwargs) return await anyio.to_thread.run_sync( functools.partial(func, *args, **kwargs), ) # inspired by `asyncer`, https://github.com/tiangolo/asyncer def asyncify(function: Callable[T_ParamSpec, T_Retval]) -> Callable[T_ParamSpec, Awaitable[T_Retval]]: """ Take a blocking function and create an async one that receives the same positional and keyword arguments. Usage: ```python def blocking_func(arg1, arg2, kwarg1=None): # blocking code return result result = asyncify(blocking_function)(arg1, arg2, kwarg1=value1) ``` ## Arguments `function`: a blocking regular callable (e.g. a function) ## Return An async function that takes the same positional and keyword arguments as the original one, that when called runs the same original function in a thread worker and returns the result. """ async def wrapper(*args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs) -> T_Retval: return await to_thread(function, *args, **kwargs) return wrapper anthropic-sdk-python-0.120.2/src/anthropic/_utils/_transform.py000066400000000000000000000373711523216435200245600ustar00rootroot00000000000000from __future__ import annotations import io import base64 import pathlib from typing import Any, Mapping, TypeVar, cast from datetime import date, datetime from typing_extensions import Literal, get_args, override, get_type_hints as _get_type_hints import anyio import pydantic from ._utils import ( is_list, is_given, lru_cache, is_mapping, is_iterable, is_sequence, ) from .._files import is_base64_file_input from ._compat import get_origin, is_typeddict from ._typing import ( is_list_type, is_union_type, extract_type_arg, is_iterable_type, is_required_type, is_sequence_type, is_annotated_type, strip_annotated_type, ) _T = TypeVar("_T") # TODO: support for drilling globals() and locals() # TODO: ensure works correctly with forward references in all cases PropertyFormat = Literal["iso8601", "base64", "custom"] class PropertyInfo: """Metadata class to be used in Annotated types to provide information about a given type. For example: class MyParams(TypedDict): account_holder_name: Annotated[str, PropertyInfo(alias='accountHolderName')] This means that {'account_holder_name': 'Robert'} will be transformed to {'accountHolderName': 'Robert'} before being sent to the API. """ alias: str | None format: PropertyFormat | None format_template: str | None discriminator: str | None def __init__( self, *, alias: str | None = None, format: PropertyFormat | None = None, format_template: str | None = None, discriminator: str | None = None, ) -> None: self.alias = alias self.format = format self.format_template = format_template self.discriminator = discriminator @override def __repr__(self) -> str: return f"{self.__class__.__name__}(alias='{self.alias}', format={self.format}, format_template='{self.format_template}', discriminator='{self.discriminator}')" def maybe_transform( data: object, expected_type: object, ) -> Any | None: """Wrapper over `transform()` that allows `None` to be passed. See `transform()` for more details. """ if data is None: return None return transform(data, expected_type) # Wrapper over _transform_recursive providing fake types def transform( data: _T, expected_type: object, ) -> _T: """Transform dictionaries based off of type information from the given type, for example: ```py class Params(TypedDict, total=False): card_id: Required[Annotated[str, PropertyInfo(alias="cardID")]] transformed = transform({"card_id": ""}, Params) # {'cardID': ''} ``` Any keys / data that does not have type information given will be included as is. It should be noted that the transformations that this function does are not represented in the type system. """ transformed = _transform_recursive(data, annotation=cast(type, expected_type)) return cast(_T, transformed) @lru_cache(maxsize=8096) def _get_annotated_type(type_: type) -> type | None: """If the given type is an `Annotated` type then it is returned, if not `None` is returned. This also unwraps the type when applicable, e.g. `Required[Annotated[T, ...]]` """ if is_required_type(type_): # Unwrap `Required[Annotated[T, ...]]` to `Annotated[T, ...]` type_ = get_args(type_)[0] if is_annotated_type(type_): return type_ return None def _maybe_transform_key(key: str, type_: type) -> str: """Transform the given `data` based on the annotations provided in `type_`. Note: this function only looks at `Annotated` types that contain `PropertyInfo` metadata. """ annotated_type = _get_annotated_type(type_) if annotated_type is None: # no `Annotated` definition for this type, no transformation needed return key # ignore the first argument as it is the actual type annotations = get_args(annotated_type)[1:] for annotation in annotations: if isinstance(annotation, PropertyInfo) and annotation.alias is not None: return annotation.alias return key def _no_transform_needed(annotation: type) -> bool: return annotation == float or annotation == int def _transform_recursive( data: object, *, annotation: type, inner_type: type | None = None, ) -> object: """Transform the given data against the expected type. Args: annotation: The direct type annotation given to the particular piece of data. This may or may not be wrapped in metadata types, e.g. `Required[T]`, `Annotated[T, ...]` etc inner_type: If applicable, this is the "inside" type. This is useful in certain cases where the outside type is a container type such as `List[T]`. In that case `inner_type` should be set to `T` so that each entry in the list can be transformed using the metadata from the container type. Defaults to the same value as the `annotation` argument. """ from .._compat import model_dump if inner_type is None: inner_type = annotation stripped_type = strip_annotated_type(inner_type) origin = get_origin(stripped_type) or stripped_type if is_typeddict(stripped_type) and is_mapping(data): return _transform_typeddict(data, stripped_type) if origin == dict and is_mapping(data): items_type = get_args(stripped_type)[1] return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()} if ( # List[T] (is_list_type(stripped_type) and is_list(data)) # Iterable[T] or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str)) # Sequence[T] or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str)) ): # dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually # intended as an iterable, so we don't transform it. if isinstance(data, dict): return cast(object, data) inner_type = extract_type_arg(stripped_type, 0) if _no_transform_needed(inner_type): # for some types there is no need to transform anything, so we can get a small # perf boost from skipping that work. # # but we still need to convert to a list to ensure the data is json-serializable if is_list(data): return data return list(data) return [_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] if is_union_type(stripped_type): # For union types we run the transformation against all subtypes to ensure that everything is transformed. # # TODO: there may be edge cases where the same normalized field name will transform to two different names # in different subtypes. for subtype in get_args(stripped_type): data = _transform_recursive(data, annotation=annotation, inner_type=subtype) return data if isinstance(data, pydantic.BaseModel): return model_dump( data, exclude_unset=True, mode="json", by_alias=True, exclude=getattr(data, "__api_exclude__", None) ) annotated_type = _get_annotated_type(annotation) if annotated_type is None: return data # ignore the first argument as it is the actual type annotations = get_args(annotated_type)[1:] for annotation in annotations: if isinstance(annotation, PropertyInfo) and annotation.format is not None: return _format_data(data, annotation.format, annotation.format_template) return data def _format_data(data: object, format_: PropertyFormat, format_template: str | None) -> object: if isinstance(data, (date, datetime)): if format_ == "iso8601": return data.isoformat() if format_ == "custom" and format_template is not None: return data.strftime(format_template) if format_ == "base64" and is_base64_file_input(data): binary: str | bytes | None = None if isinstance(data, pathlib.Path): binary = data.read_bytes() elif isinstance(data, io.IOBase): binary = data.read() if isinstance(binary, str): # type: ignore[unreachable] binary = binary.encode() if not isinstance(binary, bytes): raise RuntimeError(f"Could not read bytes from {data}; Received {type(binary)}") return base64.b64encode(binary).decode("ascii") return data def _transform_typeddict( data: Mapping[str, object], expected_type: type, ) -> Mapping[str, object]: result: dict[str, object] = {} annotations = get_type_hints(expected_type, include_extras=True) for key, value in data.items(): if not is_given(value): # we don't need to include omitted values here as they'll # be stripped out before the request is sent anyway continue type_ = annotations.get(key) if type_ is None: # we do not have a type annotation for this field, leave it as is result[key] = value else: result[_maybe_transform_key(key, type_)] = _transform_recursive(value, annotation=type_) return result async def async_maybe_transform( data: object, expected_type: object, ) -> Any | None: """Wrapper over `async_transform()` that allows `None` to be passed. See `async_transform()` for more details. """ if data is None: return None return await async_transform(data, expected_type) async def async_transform( data: _T, expected_type: object, ) -> _T: """Transform dictionaries based off of type information from the given type, for example: ```py class Params(TypedDict, total=False): card_id: Required[Annotated[str, PropertyInfo(alias="cardID")]] transformed = transform({"card_id": ""}, Params) # {'cardID': ''} ``` Any keys / data that does not have type information given will be included as is. It should be noted that the transformations that this function does are not represented in the type system. """ transformed = await _async_transform_recursive(data, annotation=cast(type, expected_type)) return cast(_T, transformed) async def _async_transform_recursive( data: object, *, annotation: type, inner_type: type | None = None, ) -> object: """Transform the given data against the expected type. Args: annotation: The direct type annotation given to the particular piece of data. This may or may not be wrapped in metadata types, e.g. `Required[T]`, `Annotated[T, ...]` etc inner_type: If applicable, this is the "inside" type. This is useful in certain cases where the outside type is a container type such as `List[T]`. In that case `inner_type` should be set to `T` so that each entry in the list can be transformed using the metadata from the container type. Defaults to the same value as the `annotation` argument. """ from .._compat import model_dump if inner_type is None: inner_type = annotation stripped_type = strip_annotated_type(inner_type) origin = get_origin(stripped_type) or stripped_type if is_typeddict(stripped_type) and is_mapping(data): return await _async_transform_typeddict(data, stripped_type) if origin == dict and is_mapping(data): items_type = get_args(stripped_type)[1] return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()} if ( # List[T] (is_list_type(stripped_type) and is_list(data)) # Iterable[T] or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str)) # Sequence[T] or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str)) ): # dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually # intended as an iterable, so we don't transform it. if isinstance(data, dict): return cast(object, data) inner_type = extract_type_arg(stripped_type, 0) if _no_transform_needed(inner_type): # for some types there is no need to transform anything, so we can get a small # perf boost from skipping that work. # # but we still need to convert to a list to ensure the data is json-serializable if is_list(data): return data return list(data) return [await _async_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data] if is_union_type(stripped_type): # For union types we run the transformation against all subtypes to ensure that everything is transformed. # # TODO: there may be edge cases where the same normalized field name will transform to two different names # in different subtypes. for subtype in get_args(stripped_type): data = await _async_transform_recursive(data, annotation=annotation, inner_type=subtype) return data if isinstance(data, pydantic.BaseModel): return model_dump( data, exclude_unset=True, mode="json", by_alias=True, exclude=getattr(data, "__api_exclude__", None) ) annotated_type = _get_annotated_type(annotation) if annotated_type is None: return data # ignore the first argument as it is the actual type annotations = get_args(annotated_type)[1:] for annotation in annotations: if isinstance(annotation, PropertyInfo) and annotation.format is not None: return await _async_format_data(data, annotation.format, annotation.format_template) return data async def _async_format_data(data: object, format_: PropertyFormat, format_template: str | None) -> object: if isinstance(data, (date, datetime)): if format_ == "iso8601": return data.isoformat() if format_ == "custom" and format_template is not None: return data.strftime(format_template) if format_ == "base64" and is_base64_file_input(data): binary: str | bytes | None = None if isinstance(data, pathlib.Path): binary = await anyio.Path(data).read_bytes() elif isinstance(data, io.IOBase): binary = data.read() if isinstance(binary, str): # type: ignore[unreachable] binary = binary.encode() if not isinstance(binary, bytes): raise RuntimeError(f"Could not read bytes from {data}; Received {type(binary)}") return base64.b64encode(binary).decode("ascii") return data async def _async_transform_typeddict( data: Mapping[str, object], expected_type: type, ) -> Mapping[str, object]: result: dict[str, object] = {} annotations = get_type_hints(expected_type, include_extras=True) for key, value in data.items(): if not is_given(value): # we don't need to include omitted values here as they'll # be stripped out before the request is sent anyway continue type_ = annotations.get(key) if type_ is None: # we do not have a type annotation for this field, leave it as is result[key] = value else: result[_maybe_transform_key(key, type_)] = await _async_transform_recursive(value, annotation=type_) return result @lru_cache(maxsize=8096) def get_type_hints( obj: Any, globalns: dict[str, Any] | None = None, localns: Mapping[str, Any] | None = None, include_extras: bool = False, ) -> dict[str, Any]: return _get_type_hints(obj, globalns=globalns, localns=localns, include_extras=include_extras) anthropic-sdk-python-0.120.2/src/anthropic/_utils/_typing.py000066400000000000000000000113141523216435200240440ustar00rootroot00000000000000from __future__ import annotations import sys import typing import typing_extensions from typing import Any, TypeVar, Iterable, cast from collections import abc as _c_abc from typing_extensions import ( TypeIs, Required, Annotated, get_args, get_origin, ) from ._utils import lru_cache from .._types import InheritsGeneric from ._compat import is_union as _is_union def is_annotated_type(typ: type) -> bool: return get_origin(typ) == Annotated def is_list_type(typ: type) -> bool: return (get_origin(typ) or typ) == list def is_sequence_type(typ: type) -> bool: origin = get_origin(typ) or typ return origin == typing_extensions.Sequence or origin == typing.Sequence or origin == _c_abc.Sequence def is_iterable_type(typ: type) -> bool: """If the given type is `typing.Iterable[T]`""" origin = get_origin(typ) or typ return origin == Iterable or origin == _c_abc.Iterable def is_union_type(typ: type) -> bool: return _is_union(get_origin(typ)) def is_required_type(typ: type) -> bool: return get_origin(typ) == Required def is_typevar(typ: type) -> bool: # type ignore is required because type checkers # think this expression will always return False return type(typ) == TypeVar # type: ignore _TYPE_ALIAS_TYPES: tuple[type[typing_extensions.TypeAliasType], ...] = (typing_extensions.TypeAliasType,) if sys.version_info >= (3, 12): _TYPE_ALIAS_TYPES = (*_TYPE_ALIAS_TYPES, typing.TypeAliasType) # type: ignore[arg-type] def is_type_alias_type(tp: Any, /) -> TypeIs[typing_extensions.TypeAliasType]: """Return whether the provided argument is an instance of `TypeAliasType`. ```python type Int = int is_type_alias_type(Int) # > True Str = TypeAliasType("Str", str) is_type_alias_type(Str) # > True ``` """ return isinstance(tp, _TYPE_ALIAS_TYPES) # Extracts T from Annotated[T, ...] or from Required[Annotated[T, ...]] @lru_cache(maxsize=8096) def strip_annotated_type(typ: type) -> type: if is_required_type(typ) or is_annotated_type(typ): return strip_annotated_type(cast(type, get_args(typ)[0])) return typ def extract_type_arg(typ: type, index: int) -> type: args = get_args(typ) try: return cast(type, args[index]) except IndexError as err: raise RuntimeError(f"Expected type {typ} to have a type argument at index {index} but it did not") from err def extract_type_var_from_base( typ: type, *, generic_bases: tuple[type, ...], index: int, failure_message: str | None = None, ) -> type: """Given a type like `Foo[T]`, returns the generic type variable `T`. This also handles the case where a concrete subclass is given, e.g. ```py class MyResponse(Foo[bytes]): ... extract_type_var(MyResponse, bases=(Foo,), index=0) -> bytes ``` And where a generic subclass is given: ```py _T = TypeVar('_T') class MyResponse(Foo[_T]): ... extract_type_var(MyResponse[bytes], bases=(Foo,), index=0) -> bytes ``` """ cls = cast(object, get_origin(typ) or typ) if cls in generic_bases: # pyright: ignore[reportUnnecessaryContains] # we're given the class directly return extract_type_arg(typ, index) # if a subclass is given # --- # this is needed as __orig_bases__ is not present in the typeshed stubs # because it is intended to be for internal use only, however there does # not seem to be a way to resolve generic TypeVars for inherited subclasses # without using it. if isinstance(cls, InheritsGeneric): target_base_class: Any | None = None for base in cls.__orig_bases__: if base.__origin__ in generic_bases: target_base_class = base break if target_base_class is None: raise RuntimeError( "Could not find the generic base class;\n" "This should never happen;\n" f"Does {cls} inherit from one of {generic_bases} ?" ) extracted = extract_type_arg(target_base_class, index) if is_typevar(extracted): # If the extracted type argument is itself a type variable # then that means the subclass itself is generic, so we have # to resolve the type argument from the class itself, not # the base class. # # Note: if there is more than 1 type argument, the subclass could # change the ordering of the type arguments, this is not currently # supported. return extract_type_arg(typ, index) return extracted raise RuntimeError(failure_message or f"Could not resolve inner type variable at index {index} for {typ}") anthropic-sdk-python-0.120.2/src/anthropic/_utils/_utils.py000066400000000000000000000314671523216435200237050ustar00rootroot00000000000000from __future__ import annotations import os import re import inspect import functools from typing import ( Any, Tuple, Mapping, TypeVar, Callable, Iterable, Sequence, cast, overload, ) from pathlib import Path from datetime import date, datetime from typing_extensions import TypeGuard, get_args import sniffio from .._types import Omit, NotGiven, FileTypes, ArrayFormat, HeadersLike _T = TypeVar("_T") _TupleT = TypeVar("_TupleT", bound=Tuple[object, ...]) _MappingT = TypeVar("_MappingT", bound=Mapping[str, object]) _SequenceT = TypeVar("_SequenceT", bound=Sequence[object]) CallableT = TypeVar("CallableT", bound=Callable[..., Any]) def flatten(t: Iterable[Iterable[_T]]) -> list[_T]: return [item for sublist in t for item in sublist] def extract_files( # TODO: this needs to take Dict but variance issues..... # create protocol type ? query: Mapping[str, object], *, paths: Sequence[Sequence[str]], array_format: ArrayFormat = "brackets", ) -> list[tuple[str, FileTypes]]: """Recursively extract files from the given dictionary based on specified paths. A path may look like this ['foo', 'files', '', 'data']. ``array_format`` controls how ```` segments contribute to the emitted field name. Supported values: ``"brackets"`` (``foo[]``), ``"repeat"`` and ``"comma"`` (``foo``), ``"indices"`` (``foo[0]``, ``foo[1]``). Note: this mutates the given dictionary. """ files: list[tuple[str, FileTypes]] = [] for path in paths: files.extend(_extract_items(query, path, index=0, flattened_key=None, array_format=array_format)) return files def _array_suffix(array_format: ArrayFormat, array_index: int) -> str: if array_format == "brackets": return "[]" if array_format == "indices": return f"[{array_index}]" if array_format == "repeat" or array_format == "comma": # Both repeat the bare field name for each file part; there is no # meaningful way to comma-join binary parts. return "" raise NotImplementedError( f"Unknown array_format value: {array_format}, choose from {', '.join(get_args(ArrayFormat))}" ) def _extract_items( obj: object, path: Sequence[str], *, index: int, flattened_key: str | None, array_format: ArrayFormat, ) -> list[tuple[str, FileTypes]]: try: key = path[index] except IndexError: if not is_given(obj): # no value was provided - we can safely ignore return [] # cyclical import from .._files import assert_is_file_content # We have exhausted the path, return the entry we found. assert flattened_key is not None if is_list(obj): files: list[tuple[str, FileTypes]] = [] for array_index, entry in enumerate(obj): suffix = _array_suffix(array_format, array_index) emitted_key = (flattened_key + suffix) if flattened_key else suffix assert_is_file_content(entry, key=emitted_key) files.append((emitted_key, cast(FileTypes, entry))) return files assert_is_file_content(obj, key=flattened_key) return [(flattened_key, cast(FileTypes, obj))] index += 1 if is_dict(obj): try: # Remove the field if there are no more dict keys in the path, # only "" traversal markers or end. if all(p == "" for p in path[index:]): item = obj.pop(key) else: item = obj[key] except KeyError: # Key was not present in the dictionary, this is not indicative of an error # as the given path may not point to a required field. We also do not want # to enforce required fields as the API may differ from the spec in some cases. return [] if flattened_key is None: flattened_key = key else: flattened_key += f"[{key}]" return _extract_items( item, path, index=index, flattened_key=flattened_key, array_format=array_format, ) elif is_list(obj): if key != "": return [] return flatten( [ _extract_items( item, path, index=index, flattened_key=( (flattened_key if flattened_key is not None else "") + _array_suffix(array_format, array_index) ), array_format=array_format, ) for array_index, item in enumerate(obj) ] ) # Something unexpected was passed, just ignore it. return [] def is_given(obj: _T | NotGiven | Omit) -> TypeGuard[_T]: return not isinstance(obj, NotGiven) and not isinstance(obj, Omit) # Type safe methods for narrowing types with TypeVars. # The default narrowing for isinstance(obj, dict) is dict[unknown, unknown], # however this cause Pyright to rightfully report errors. As we know we don't # care about the contained types we can safely use `object` in its place. # # There are two separate functions defined, `is_*` and `is_*_t` for different use cases. # `is_*` is for when you're dealing with an unknown input # `is_*_t` is for when you're narrowing a known union type to a specific subset def is_tuple(obj: object) -> TypeGuard[tuple[object, ...]]: return isinstance(obj, tuple) def is_tuple_t(obj: _TupleT | object) -> TypeGuard[_TupleT]: return isinstance(obj, tuple) def is_sequence(obj: object) -> TypeGuard[Sequence[object]]: return isinstance(obj, Sequence) def is_sequence_t(obj: _SequenceT | object) -> TypeGuard[_SequenceT]: return isinstance(obj, Sequence) def is_mapping(obj: object) -> TypeGuard[Mapping[str, object]]: return isinstance(obj, Mapping) def is_mapping_t(obj: _MappingT | object) -> TypeGuard[_MappingT]: return isinstance(obj, Mapping) def is_dict(obj: object) -> TypeGuard[dict[object, object]]: return isinstance(obj, dict) def is_list(obj: object) -> TypeGuard[list[object]]: return isinstance(obj, list) def is_iterable(obj: object) -> TypeGuard[Iterable[object]]: return isinstance(obj, Iterable) # copied from https://github.com/Rapptz/RoboDanny def human_join(seq: Sequence[str], *, delim: str = ", ", final: str = "or") -> str: size = len(seq) if size == 0: return "" if size == 1: return seq[0] if size == 2: return f"{seq[0]} {final} {seq[1]}" return delim.join(seq[:-1]) + f" {final} {seq[-1]}" def quote(string: str) -> str: """Add single quotation marks around the given string. Does *not* do any escaping.""" return f"'{string}'" def required_args(*variants: Sequence[str]) -> Callable[[CallableT], CallableT]: """Decorator to enforce a given set of arguments or variants of arguments are passed to the decorated function. Useful for enforcing runtime validation of overloaded functions. Example usage: ```py @overload def foo(*, a: str) -> str: ... @overload def foo(*, b: bool) -> str: ... # This enforces the same constraints that a static type checker would # i.e. that either a or b must be passed to the function @required_args(["a"], ["b"]) def foo(*, a: str | None = None, b: bool | None = None) -> str: ... ``` """ def inner(func: CallableT) -> CallableT: params = inspect.signature(func).parameters positional = [ name for name, param in params.items() if param.kind in { param.POSITIONAL_ONLY, param.POSITIONAL_OR_KEYWORD, } ] @functools.wraps(func) def wrapper(*args: object, **kwargs: object) -> object: given_params: set[str] = set() for i, _ in enumerate(args): try: given_params.add(positional[i]) except IndexError: raise TypeError( f"{func.__name__}() takes {len(positional)} argument(s) but {len(args)} were given" ) from None for key in kwargs.keys(): given_params.add(key) for variant in variants: matches = all((param in given_params for param in variant)) if matches: break else: # no break if len(variants) > 1: variations = human_join( ["(" + human_join([quote(arg) for arg in variant], final="and") + ")" for variant in variants] ) msg = f"Missing required arguments; Expected either {variations} arguments to be given" else: assert len(variants) > 0 # TODO: this error message is not deterministic missing = list(set(variants[0]) - given_params) if len(missing) > 1: msg = f"Missing required arguments: {human_join([quote(arg) for arg in missing])}" else: msg = f"Missing required argument: {quote(missing[0])}" raise TypeError(msg) return func(*args, **kwargs) return wrapper # type: ignore return inner _K = TypeVar("_K") _V = TypeVar("_V") @overload def strip_not_given(obj: None) -> None: ... @overload def strip_not_given(obj: Mapping[_K, _V | NotGiven]) -> dict[_K, _V]: ... @overload def strip_not_given(obj: object) -> object: ... def strip_not_given(obj: object | None) -> object: """Remove all top-level keys where their values are instances of `NotGiven`""" if obj is None: return None if not is_mapping(obj): return obj return {key: value for key, value in obj.items() if not isinstance(value, NotGiven)} def coerce_integer(val: str) -> int: return int(val, base=10) def coerce_float(val: str) -> float: return float(val) def coerce_boolean(val: str) -> bool: return val == "true" or val == "1" or val == "on" def maybe_coerce_integer(val: str | None) -> int | None: if val is None: return None return coerce_integer(val) def maybe_coerce_float(val: str | None) -> float | None: if val is None: return None return coerce_float(val) def maybe_coerce_boolean(val: str | None) -> bool | None: if val is None: return None return coerce_boolean(val) def removeprefix(string: str, prefix: str) -> str: """Remove a prefix from a string. Backport of `str.removeprefix` for Python < 3.9 """ if string.startswith(prefix): return string[len(prefix) :] return string def removesuffix(string: str, suffix: str) -> str: """Remove a suffix from a string. Backport of `str.removesuffix` for Python < 3.9 """ if string.endswith(suffix): return string[: -len(suffix)] return string def file_from_path(path: str) -> FileTypes: contents = Path(path).read_bytes() file_name = os.path.basename(path) return (file_name, contents) def get_required_header(headers: HeadersLike, header: str) -> str: lower_header = header.lower() if is_mapping_t(headers): # mypy doesn't understand the type narrowing here for k, v in headers.items(): # type: ignore if k.lower() == lower_header and isinstance(v, str): return v # to deal with the case where the header looks like Stainless-Event-Id intercaps_header = re.sub(r"([^\w])(\w)", lambda pat: pat.group(1) + pat.group(2).upper(), header.capitalize()) for normalized_header in [header, lower_header, header.upper(), intercaps_header]: value = headers.get(normalized_header) if value: return value raise ValueError(f"Could not find {header} header") def get_async_library() -> str: try: return sniffio.current_async_library() except Exception: return "false" def lru_cache(*, maxsize: int | None = 128) -> Callable[[CallableT], CallableT]: """A version of functools.lru_cache that retains the type signature for the wrapped function arguments. """ wrapper = functools.lru_cache( # noqa: TID251 maxsize=maxsize, ) return cast(Any, wrapper) # type: ignore[no-any-return] def json_safe(data: object) -> object: """Translates a mapping / sequence recursively in the same fashion as `pydantic` v2's `model_dump(mode="json")`. """ if is_mapping(data): return {json_safe(key): json_safe(value) for key, value in data.items()} if is_iterable(data) and not isinstance(data, (str, bytes, bytearray)): return [json_safe(item) for item in data] if isinstance(data, (datetime, date)): return data.isoformat() return data anthropic-sdk-python-0.120.2/src/anthropic/_version.py000066400000000000000000000002431523216435200227170ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "anthropic" __version__ = "0.120.2" # x-release-please-version anthropic-sdk-python-0.120.2/src/anthropic/lib/000077500000000000000000000000001523216435200212705ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/lib/.keep000066400000000000000000000003401523216435200222120ustar00rootroot00000000000000File generated from our OpenAPI spec by Stainless. This directory can be used to store custom files to expand the SDK. It is ignored by Stainless code generation and its content (other than this keep file) won't be touched.anthropic-sdk-python-0.120.2/src/anthropic/lib/__init__.py000066400000000000000000000001431523216435200233770ustar00rootroot00000000000000from ._files import files_from_dir as files_from_dir, async_files_from_dir as async_files_from_dir anthropic-sdk-python-0.120.2/src/anthropic/lib/_extras/000077500000000000000000000000001523216435200227355ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/lib/_extras/__init__.py000066400000000000000000000000651523216435200250470ustar00rootroot00000000000000from ._google_auth import google_auth as google_auth anthropic-sdk-python-0.120.2/src/anthropic/lib/_extras/_common.py000066400000000000000000000005341523216435200247400ustar00rootroot00000000000000from ..._exceptions import AnthropicError INSTRUCTIONS = """ Anthropic error: missing required dependency `{library}`. $ pip install anthropic[{extra}] """ class MissingDependencyError(AnthropicError): def __init__(self, *, library: str, extra: str) -> None: super().__init__(INSTRUCTIONS.format(library=library, extra=extra)) anthropic-sdk-python-0.120.2/src/anthropic/lib/_extras/_google_auth.py000066400000000000000000000050221523216435200257420ustar00rootroot00000000000000from __future__ import annotations from typing import TYPE_CHECKING, Any, cast from typing_extensions import ClassVar, override from ._common import MissingDependencyError from ..._utils import LazyProxy if TYPE_CHECKING: import google.auth # type: ignore from google.auth.credentials import Credentials as GoogleCredentials # type: ignore google_auth = google.auth # pyright: reportMissingTypeStubs=false, reportUnknownVariableType=false, reportUnknownMemberType=false, reportUnknownArgumentType=false # google libraries don't ship type stubs. CLOUD_PLATFORM_SCOPE = "https://www.googleapis.com/auth/cloud-platform" class GoogleAuthProxy(LazyProxy[Any]): should_cache: ClassVar[bool] = True @override def __load__(self) -> Any: try: import google.auth # type: ignore except ImportError as err: raise MissingDependencyError(extra="vertex", library="google-auth") from err return google.auth if not TYPE_CHECKING: google_auth = GoogleAuthProxy() def _request(*, extra: str = "vertex") -> Any: try: from google.auth.transport.requests import Request # type: ignore[import-untyped] except ImportError as err: raise MissingDependencyError(extra=extra, library="google-auth") from err return Request() def load_default_credentials(*, extra: str = "vertex") -> tuple[GoogleCredentials, str | None]: """Load Application Default Credentials with the ``cloud-platform`` scope and mint an initial access token. Returns the credentials object and the project they resolve to (``None`` for plain user ADC). Blocking — async callers wrap with :func:`anthropic._utils.asyncify`. ``extra`` names the pip extra that the install hint in :class:`MissingDependencyError` points at when ``google-auth`` isn't installed; callers pass the extra for their client. """ try: import google.auth # type: ignore except ImportError as err: raise MissingDependencyError(extra=extra, library="google-auth") from err credentials, project = google.auth.default(scopes=[CLOUD_PLATFORM_SCOPE]) cast(Any, credentials).refresh(_request(extra=extra)) return cast("GoogleCredentials", credentials), project def refresh_credentials(credentials: GoogleCredentials, *, extra: str = "vertex") -> None: """Refresh ``credentials`` in place via ``google.auth.transport.requests``. Blocking — async callers wrap with :func:`anthropic._utils.asyncify`. """ cast(Any, credentials).refresh(_request(extra=extra)) anthropic-sdk-python-0.120.2/src/anthropic/lib/_files.py000066400000000000000000000023051523216435200231030ustar00rootroot00000000000000from __future__ import annotations import os from pathlib import Path import anyio from .._types import FileTypes def files_from_dir(directory: str | os.PathLike[str]) -> list[FileTypes]: path = Path(directory) files: list[FileTypes] = [] _collect_files(path, path.parent, files) return files def _collect_files(directory: Path, relative_to: Path, files: list[FileTypes]) -> None: for path in directory.iterdir(): if path.is_dir(): _collect_files(path, relative_to, files) continue files.append((path.relative_to(relative_to).as_posix(), path.read_bytes())) async def async_files_from_dir(directory: str | os.PathLike[str]) -> list[FileTypes]: path = anyio.Path(directory) files: list[FileTypes] = [] await _async_collect_files(path, path.parent, files) return files async def _async_collect_files(directory: anyio.Path, relative_to: anyio.Path, files: list[FileTypes]) -> None: async for path in directory.iterdir(): if await path.is_dir(): await _async_collect_files(path, relative_to, files) continue files.append((path.relative_to(relative_to).as_posix(), await path.read_bytes())) anthropic-sdk-python-0.120.2/src/anthropic/lib/_parse/000077500000000000000000000000001523216435200225415ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/lib/_parse/_response.py000066400000000000000000000046341523216435200251170ustar00rootroot00000000000000from __future__ import annotations from typing_extensions import TypeVar from ..._types import NotGiven from ..._models import TypeAdapter, construct_type_unchecked from ..._utils._utils import is_given from ...types.message import Message from ...types.parsed_message import ParsedMessage, ParsedTextBlock, ParsedContentBlock from ...types.beta.beta_message import BetaMessage from ...types.beta.parsed_beta_message import ParsedBetaMessage, ParsedBetaTextBlock, ParsedBetaContentBlock ResponseFormatT = TypeVar("ResponseFormatT", default=None) def parse_text(text: str, output_format: ResponseFormatT | NotGiven) -> ResponseFormatT | None: if is_given(output_format): adapted_type: TypeAdapter[ResponseFormatT] = TypeAdapter(output_format) return adapted_type.validate_json(text) return None def parse_beta_response( *, output_format: ResponseFormatT | NotGiven, response: BetaMessage, ) -> ParsedBetaMessage[ResponseFormatT]: content_list: list[ParsedBetaContentBlock[ResponseFormatT]] = [] for content in response.content: if content.type == "text": content_list.append( construct_type_unchecked( type_=ParsedBetaTextBlock[ResponseFormatT], value={**content.to_dict(), "parsed_output": parse_text(content.text, output_format)}, ) ) else: content_list.append(content) # type: ignore return construct_type_unchecked( type_=ParsedBetaMessage[ResponseFormatT], value={ **response.to_dict(), "content": content_list, }, ) def parse_response( *, output_format: ResponseFormatT | NotGiven, response: Message, ) -> ParsedMessage[ResponseFormatT]: content_list: list[ParsedContentBlock[ResponseFormatT]] = [] for content in response.content: if content.type == "text": content_list.append( construct_type_unchecked( type_=ParsedTextBlock[ResponseFormatT], value={**content.to_dict(), "parsed_output": parse_text(content.text, output_format)}, ) ) else: content_list.append(content) # type: ignore return construct_type_unchecked( type_=ParsedMessage[ResponseFormatT], value={ **response.to_dict(), "content": content_list, }, ) anthropic-sdk-python-0.120.2/src/anthropic/lib/_parse/_transform.py000066400000000000000000000127431523216435200252740ustar00rootroot00000000000000from __future__ import annotations import inspect from typing import Any, Literal, Optional, cast from typing_extensions import assert_never import pydantic from ..._utils import is_list SupportedTypes = Literal[ "object", "array", "string", "integer", "number", "boolean", "null", ] SupportedStringFormats = { "date-time", "time", "date", "duration", "email", "hostname", "uri", "ipv4", "ipv6", "uuid", } def get_transformed_string( schema: dict[str, Any], ) -> dict[str, Any]: """Transforms a JSON schema of type string to ensure it conforms to the API's expectations. Specifically, it ensures that if the schema is of type "string" and does not already specify a "format", it sets the format to "text". Args: schema: The original JSON schema. Returns: The transformed JSON schema. """ if schema.get("type") == "string" and "format" not in schema: schema["format"] = "text" return schema def transform_schema( json_schema: type[pydantic.BaseModel] | dict[str, Any], ) -> dict[str, Any]: """ Transforms a JSON schema to ensure it conforms to the API's expectations. Args: json_schema (Dict[str, Any]): The original JSON schema. Returns: The transformed JSON schema. Examples: >>> transform_schema( ... { ... "type": "integer", ... "minimum": 1, ... "maximum": 10, ... "description": "A number", ... } ... ) {'type': 'integer', 'description': 'A number\n\n{minimum: 1, maximum: 10}'} """ if inspect.isclass(json_schema) and issubclass(json_schema, pydantic.BaseModel): # pyright: ignore[reportUnnecessaryIsInstance] json_schema = json_schema.model_json_schema() strict_schema: dict[str, Any] = {} json_schema = {**json_schema} # $defs must be processed before the $ref early-return below, so that a # root-level `{"$ref": "#/$defs/X", "$defs": {...}}` (valid JSON Schema, # and what pydantic RootModel emits) keeps its definitions. defs = json_schema.pop("$defs", None) if defs is not None: strict_defs: dict[str, Any] = {} strict_schema["$defs"] = strict_defs for name, schema in defs.items(): strict_defs[name] = transform_schema(schema) ref = json_schema.pop("$ref", None) if ref is not None: strict_schema["$ref"] = ref return strict_schema type_: Optional[SupportedTypes] = json_schema.pop("type", None) any_of = json_schema.pop("anyOf", None) one_of = json_schema.pop("oneOf", None) all_of = json_schema.pop("allOf", None) if is_list(any_of): strict_schema["anyOf"] = [transform_schema(cast("dict[str, Any]", variant)) for variant in any_of] elif is_list(one_of): strict_schema["anyOf"] = [transform_schema(cast("dict[str, Any]", variant)) for variant in one_of] elif is_list(all_of): strict_schema["allOf"] = [transform_schema(cast("dict[str, Any]", variant)) for variant in all_of] else: if type_ is None: raise ValueError("Schema must have a 'type', 'anyOf', 'oneOf', or 'allOf' field.") strict_schema["type"] = type_ enum = json_schema.pop("enum", None) if is_list(enum): strict_schema["enum"] = enum description = json_schema.pop("description", None) if description is not None: strict_schema["description"] = description title = json_schema.pop("title", None) if title is not None: strict_schema["title"] = title if type_ == "object": strict_schema["properties"] = { key: transform_schema(prop_schema) for key, prop_schema in json_schema.pop("properties", {}).items() } json_schema.pop("additionalProperties", None) strict_schema["additionalProperties"] = False required = json_schema.pop("required", None) if required is not None: strict_schema["required"] = required elif type_ == "string": format = json_schema.pop("format", None) if format and format in SupportedStringFormats: strict_schema["format"] = format elif format: # add it back so its treated as an extra property and appended to the description json_schema["format"] = format elif type_ == "array": items = json_schema.pop("items", None) if items is not None: strict_schema["items"] = transform_schema(items) min_items = json_schema.pop("minItems", None) if min_items is not None and min_items == 0 or min_items == 1: strict_schema["minItems"] = min_items elif min_items is not None: # add it back so its treated as an extra property and appended to the description json_schema["minItems"] = min_items elif type_ == "boolean" or type_ == "integer" or type_ == "number" or type_ == "null" or type_ is None: pass else: assert_never(type_) # if there are any propes leftover then they aren't supported, so we add them to the description # so that the model *might* follow them. if json_schema: description = strict_schema.get("description") strict_schema["description"] = ( (description + "\n\n" if description is not None else "") + "{" + ", ".join(f"{key}: {value}" for key, value in json_schema.items()) + "}" ) return strict_schema anthropic-sdk-python-0.120.2/src/anthropic/lib/_retry.py000066400000000000000000000037331523216435200231540ustar00rootroot00000000000000"""Shared backoff / jitter / retry-classification helpers for the runner helpers. Extracted so the control-plane poller, the session tool runner, and the worker heartbeat all compute backoff and classify retryable failures the same way. Consumed by the runner helpers only. """ from __future__ import annotations import random import httpx from .._exceptions import APIError, APIStatusError __all__ = ["backoff", "jitter", "is_fatal_status_error", "TRANSIENT_ERRORS"] # The only exceptions a runner-helper retry loop should swallow and retry: # transport-level httpx failures (connect/read timeouts, connection resets) and # any SDK API error (covers APIConnectionError / APITimeoutError / APIStatusError # — the 4xx-vs-transient split is then made by ``is_fatal_status_error``). # Anything else (AttributeError, KeyError, …) is a real bug and must propagate # instead of being silently retried forever. TRANSIENT_ERRORS: tuple[type[Exception], ...] = (httpx.HTTPError, APIError) # 4xx codes that are still worth retrying: request timeout, conflict, and rate # limit. This matches the core client's retry policy — notably 409 is retryable # there, so the runner helpers must not treat it as fatal either. _RETRYABLE_4XX = frozenset({408, 409, 429}) def backoff(attempt: int, *, cap: float, base: float = 2.0) -> float: """Exponential backoff for ``attempt`` (1-indexed), capped at ``cap``.""" return min(cap, base**attempt) def jitter(low: float, high: float) -> float: """Uniform random delay in ``[low, high)`` — spreads out retry storms.""" return random.uniform(low, high) def is_fatal_status_error(err: Exception) -> bool: """True for a 4xx that retrying will not fix (bad key, missing resource). Aligns with the core client's ``_should_retry`` policy: 408 / 409 / 429 are transient and worth retrying; every other 4xx is fatal. """ return isinstance(err, APIStatusError) and 400 <= err.status_code < 500 and err.status_code not in _RETRYABLE_4XX anthropic-sdk-python-0.120.2/src/anthropic/lib/_scoped_client.py000066400000000000000000000061501523216435200246160ustar00rootroot00000000000000"""Shared util for building a Bearer-only sub-client for a helper. Several helpers (the environment poller, the environment worker, the session tool runner) need to issue requests authenticated by a per-helper credential (a self-hosted environment key, today) rather than the parent client's own ``X-Api-Key``. They each want to inherit the parent's full configuration — ``timeout``, ``max_retries``, ``http_client``, custom ``default_headers``, ``default_query`` — and override only the auth bits, plus tag every request with their own ``x-stainless-helper`` value. :func:`_copy_client_with_bearer_auth` is the one shared construction. """ from __future__ import annotations from typing import TYPE_CHECKING, Dict, TypeVar, cast from ._stainless_helpers import STAINLESS_HELPER_HEADER, StainlessHelperHeaderValue if TYPE_CHECKING: from .._client import Anthropic, AsyncAnthropic __all__ = ["_copy_client_with_bearer_auth"] ClientT = TypeVar("ClientT", "Anthropic", "AsyncAnthropic") def _copy_client_with_bearer_auth(client: ClientT, *, auth_token: str, helper: StainlessHelperHeaderValue) -> ClientT: """Return a copy of ``client`` authenticated with ``auth_token`` as Bearer. The returned sub-client inherits the parent's full configuration via ``client.copy()`` (``base_url``, ``timeout``, ``max_retries``, ``http_client``, ``default_query``, and any custom ``default_headers``). Overrides applied: - ``auth_token=auth_token`` — the new credential. - ``credentials=None`` — any inherited credentials provider is cleared so the bearer is the unambiguous auth. - ``default_headers`` merges in ``x-stainless-helper: `` so every request the sub-client issues is tagged for SDK telemetry without per-call plumbing. - ``api_key=None`` — the parent's ``X-Api-Key`` is cleared via a post-hoc mutation; today's ``copy()`` treats ``api_key=None`` as "inherit" via truthy-or, so the assignment is the only way to drop the parent's API key from the sub-client. - Any inherited ``Authorization`` / ``X-Api-Key`` entries in the parent's custom default-headers are stripped from the sub-client. They would otherwise win over the bearer we just set, because :meth:`AsyncAnthropic.default_headers` merges ``_custom_headers`` after ``auth_headers`` (and so beats the ``Authorization`` value produced by ``auth_token``). """ if not auth_token: raise ValueError(f"Expected a non-empty value for `auth_token` but received {auth_token!r}") scoped = client.copy( auth_token=auth_token, credentials=None, default_headers={STAINLESS_HELPER_HEADER: helper}, ) scoped.api_key = None # ``_custom_headers`` is typed as ``Mapping[str, str]`` (immutable # interface) but is constructed as a plain ``dict`` at runtime — cast # so we can ``pop()`` keys without re-typing the base client. custom: Dict[str, str] = cast("Dict[str, str]", scoped._custom_headers) for key in list(custom): if key.lower() in ("authorization", "x-api-key"): custom.pop(key) return scoped anthropic-sdk-python-0.120.2/src/anthropic/lib/_stainless_helpers.py000066400000000000000000000107441523216435200255360ustar00rootroot00000000000000"""Tracking for SDK helper usage via the x-stainless-helper header. This module is the single source of truth for the helper-telemetry header keys and the closed tag vocabulary. The append-don't-clobber merge for the header itself lives in :func:`anthropic._base_client.merge_headers`; here we only carry the constants and the per-object tagging machinery. """ from __future__ import annotations from typing import Any, cast from typing_extensions import Literal __all__ = [ "STAINLESS_HELPER_HEADER", "STAINLESS_HELPER_METHOD_HEADER", "STAINLESS_STREAM_HELPER_HEADER", "HELPER_METHOD_STREAM", "StainlessHelperHeaderValue", "helper_header", "tag_helper", "get_helper_tag", "collect_helpers", "stainless_helper_header", "stainless_helper_header_from_file", ] STAINLESS_HELPER_HEADER = "x-stainless-helper" """Telemetry header naming the SDK helper(s) a request came from. Always this lowercase form. ``merge_headers`` matches this key case-insensitively for its append semantics, but a single canonical casing keeps every call site greppable and avoids two literal casings of the same key reaching a plain dict merge anywhere upstream of it. """ STAINLESS_HELPER_METHOD_HEADER = "x-stainless-helper-method" """Telemetry header naming the SDK method (e.g. ``stream``) in use.""" STAINLESS_STREAM_HELPER_HEADER = "x-stainless-stream-helper" """Telemetry header naming the streaming surface (e.g. ``beta.messages``).""" HELPER_METHOD_STREAM = "stream" StainlessHelperHeaderValue = Literal[ "beta.messages.parse", "BetaToolRunner", "compaction", "environments-work-poller", "environments-worker", "fallback-refusal-middleware", "mcp_content", "mcp_message", "mcp_resource_to_content", "mcp_resource_to_file", "mcp_tool", "messages.parse", "session-tool-runner", ] """The closed set of helper telemetry tags, shared verbatim across SDKs. Constrained so a typo at any call site is a type error rather than silently mistagged telemetry. Existing values keep their original spellings — telemetry consumers match on them, so renames lose history. New tags are hyphenated lowercase; add them here (and to the matching set in every other SDK) before using them. """ def helper_header(value: StainlessHelperHeaderValue) -> dict[str, str]: """The ``x-stainless-helper: `` header dict, for passing into a ``merge_headers`` call or as ``extra_headers``/``default_headers``. Typing keeps the value drawn from the closed vocabulary above. """ return {STAINLESS_HELPER_HEADER: value} _HELPER_ATTR = "_stainless_helper" def tag_helper(obj: Any, name: StainlessHelperHeaderValue) -> None: """Mark an object as created by a named SDK helper.""" try: object.__setattr__(obj, _HELPER_ATTR, name) except (AttributeError, TypeError): pass def get_helper_tag(obj: object) -> str | None: """Get the helper name from an object, if any.""" return getattr(obj, _HELPER_ATTR, None) # type: ignore[return-value] def collect_helpers( tools: Any = None, messages: Any = None, ) -> list[str]: """Collect deduplicated helper names from tools and messages.""" helpers: list[str] = [] def _add(tag: str | None) -> None: if tag is not None and tag not in helpers: helpers.append(tag) if tools: for tool in tools: _add(get_helper_tag(tool)) if messages: for message in messages: _add(get_helper_tag(message)) # Check content blocks within messages if isinstance(message, dict): blocks: Any = cast(dict[str, Any], message).get("content") else: blocks = getattr(message, "content", None) if isinstance(blocks, list): for block in cast(list[object], blocks): _add(get_helper_tag(block)) return helpers def stainless_helper_header( tools: Any = None, messages: Any = None, ) -> dict[str, str]: """Build x-stainless-helper header dict from tools and messages. Returns an empty dict if no helpers are found. """ helpers = collect_helpers(tools, messages) if not helpers: return {} return {STAINLESS_HELPER_HEADER: ", ".join(helpers)} def stainless_helper_header_from_file(file: object) -> dict[str, str]: """Build x-stainless-helper header dict from a file object.""" tag = get_helper_tag(file) if tag is None: return {} return {STAINLESS_HELPER_HEADER: tag} anthropic-sdk-python-0.120.2/src/anthropic/lib/aws/000077500000000000000000000000001523216435200220625ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/lib/aws/__init__.py000066400000000000000000000001321523216435200241670ustar00rootroot00000000000000from ._client import AnthropicAWS as AnthropicAWS, AsyncAnthropicAWS as AsyncAnthropicAWS anthropic-sdk-python-0.120.2/src/anthropic/lib/aws/_auth.py000066400000000000000000000036411523216435200235400ustar00rootroot00000000000000from __future__ import annotations from typing import TYPE_CHECKING import httpx from ..._utils import lru_cache if TYPE_CHECKING: import boto3 @lru_cache(maxsize=512) def _get_session( *, aws_access_key: str | None, aws_secret_key: str | None, aws_session_token: str | None, region: str | None, profile: str | None, ) -> boto3.Session: import boto3 return boto3.Session( profile_name=profile, region_name=region, aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_key, aws_session_token=aws_session_token, ) def get_auth_headers( *, method: str, url: str, headers: httpx.Headers, aws_access_key: str | None, aws_secret_key: str | None, aws_session_token: str | None, region: str | None, profile: str | None, data: str | None, service_name: str, ) -> dict[str, str]: from botocore.auth import SigV4Auth from botocore.awsrequest import AWSRequest session = _get_session( profile=profile, region=region, aws_access_key=aws_access_key, aws_secret_key=aws_secret_key, aws_session_token=aws_session_token, ) # The connection header may be stripped by a proxy somewhere, so the receiver # of this message may not see this header, so we remove it from the set of headers # that are signed. new_headers = {k: v for k, v in dict(headers).items() if k.lower() != "connection"} request = AWSRequest(method=method.upper(), url=url, headers=new_headers, data=data) credentials = session.get_credentials() if not credentials: raise RuntimeError("Could not resolve AWS credentials from session") signer = SigV4Auth(credentials, service_name, session.region_name) signer.add_auth(request) prepped = request.prepare() return {key: value for key, value in dict(prepped.headers).items() if value is not None} anthropic-sdk-python-0.120.2/src/anthropic/lib/aws/_client.py000066400000000000000000000415511523216435200240570ustar00rootroot00000000000000from __future__ import annotations from typing import Any, Mapping, Sequence from typing_extensions import Self, override import httpx from ..._types import NOT_GIVEN, Omit, Headers, Timeout, NotGiven from ..._client import Anthropic, AsyncAnthropic from ._credentials import ( resolve_region, resolve_api_key, resolve_base_url, resolve_auth_mode, resolve_workspace_id, validate_credentials, ) from ..._exceptions import AnthropicError from ..._middleware import MiddlewareInput from ..._base_client import DEFAULT_MAX_RETRIES from ..credentials._types import AccessTokenProvider class AnthropicAWS(Anthropic): aws_access_key: str | None aws_secret_key: str | None aws_region: str | None aws_profile: str | None aws_session_token: str | None workspace_id: str | None _use_sigv4: bool _skip_auth: bool def __init__( self, *, api_key: str | None = None, aws_access_key: str | None = None, aws_secret_key: str | None = None, aws_region: str | None = None, aws_profile: str | None = None, aws_session_token: str | None = None, workspace_id: str | None = None, skip_auth: bool = False, base_url: str | httpx.URL | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, http_client: httpx.Client | None = None, middleware: Sequence[MiddlewareInput] | None = None, _strict_response_validation: bool = False, # Passed through to parent but not used for AWS auth auth_token: str | None = None, webhook_key: str | None = None, ) -> None: self._skip_auth = skip_auth validate_credentials(aws_access_key=aws_access_key, aws_secret_key=aws_secret_key) if skip_auth: self._use_sigv4 = False resolved_api_key = None else: self._use_sigv4 = resolve_auth_mode( api_key=api_key, aws_access_key=aws_access_key, aws_secret_key=aws_secret_key, aws_profile=aws_profile, ) resolved_api_key = resolve_api_key(api_key=api_key, use_sigv4=self._use_sigv4) resolved_region = resolve_region(aws_region) if self._use_sigv4 and resolved_region is None: raise AnthropicError( "No AWS region was provided. Set the `aws_region` argument or the `AWS_REGION`/`AWS_DEFAULT_REGION` environment variable." ) self.aws_access_key = aws_access_key self.aws_secret_key = aws_secret_key self.aws_region = resolved_region self.aws_profile = aws_profile self.aws_session_token = aws_session_token if skip_auth: self.workspace_id = workspace_id else: resolved_workspace_id = resolve_workspace_id(workspace_id) if resolved_workspace_id is None: raise AnthropicError( "No workspace ID found. Set the `workspace_id` argument or the `ANTHROPIC_AWS_WORKSPACE_ID` environment variable." ) self.workspace_id = resolved_workspace_id if not skip_auth: resolved_base_url = resolve_base_url( str(base_url) if base_url is not None else None, region=resolved_region, ) if resolved_base_url is None: raise AnthropicError( "No AWS region was provided and no base_url was given. " "Set the `aws_region` argument, the `AWS_REGION`/`AWS_DEFAULT_REGION` environment variable, " "or provide a `base_url` directly." ) base_url = resolved_base_url super().__init__( api_key=resolved_api_key, auth_token=auth_token, webhook_key=webhook_key, base_url=base_url, # type: ignore[arg-type] timeout=timeout, max_retries=max_retries, default_headers=default_headers, default_query=default_query, http_client=http_client, middleware=middleware, _strict_response_validation=_strict_response_validation, ) @property @override def default_headers(self) -> dict[str, str | Omit]: headers = {**super().default_headers} if self.workspace_id is not None: headers["anthropic-workspace-id"] = self.workspace_id return headers @property @override def _api_key_auth(self) -> dict[str, str]: if self._use_sigv4 or self._skip_auth: return {} return super()._api_key_auth @override def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: if self._use_sigv4 or self._skip_auth: return super()._validate_headers(headers, custom_headers) @override def _prepare_request(self, request: httpx.Request) -> None: if not self._use_sigv4: return from ._auth import get_auth_headers data = request.read().decode() headers = get_auth_headers( method=request.method, url=str(request.url), headers=request.headers, aws_access_key=self.aws_access_key, aws_secret_key=self.aws_secret_key, aws_session_token=self.aws_session_token, region=self.aws_region, profile=self.aws_profile, data=data, service_name="aws-external-anthropic", ) request.headers.update(headers) @override def copy( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] — narrows `credentials` to None-only self, *, api_key: str | None = None, aws_access_key: str | None = None, aws_secret_key: str | None = None, aws_region: str | None = None, aws_profile: str | None = None, aws_session_token: str | None = None, workspace_id: str | None = None, skip_auth: bool | None = None, auth_token: str | None = None, credentials: AccessTokenProvider | None = None, webhook_key: str | None = None, base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, http_client: httpx.Client | None = None, max_retries: int | NotGiven = NOT_GIVEN, default_headers: Mapping[str, str] | None = None, set_default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, set_default_query: Mapping[str, object] | None = None, middleware: Sequence[MiddlewareInput] | None | NotGiven = NOT_GIVEN, _extra_kwargs: Mapping[str, Any] = {}, ) -> Self: # The AWS client authenticates with SigV4 (or an API key), not a token # provider, so it has no `credentials`. Accept the argument for signature # compatibility with the base client — internal helpers such as # `_copy_client_with_bearer_auth` call `copy(credentials=None, ...)` — but # only as a no-op; reject a real provider rather than silently ignoring it. if credentials is not None: raise TypeError("AnthropicAWS does not support a `credentials` provider (it authenticates with AWS SigV4).") # If region is changing and no explicit base_url, let __init__ derive it resolved_base_url = base_url or (None if aws_region else self.base_url) return super().copy( api_key=api_key or self.api_key, auth_token=auth_token, webhook_key=webhook_key, base_url=resolved_base_url, timeout=timeout, http_client=http_client, max_retries=max_retries, default_headers=default_headers, set_default_headers=set_default_headers, default_query=default_query, set_default_query=set_default_query, middleware=middleware, _extra_kwargs={ "aws_access_key": aws_access_key or self.aws_access_key, "aws_secret_key": aws_secret_key or self.aws_secret_key, "aws_region": aws_region or self.aws_region, "aws_profile": aws_profile or self.aws_profile, "aws_session_token": aws_session_token or self.aws_session_token, "workspace_id": workspace_id or self.workspace_id, "skip_auth": skip_auth if skip_auth is not None else self._skip_auth, **_extra_kwargs, }, ) with_options = copy # type: ignore[assignment] class AsyncAnthropicAWS(AsyncAnthropic): aws_access_key: str | None aws_secret_key: str | None aws_region: str | None aws_profile: str | None aws_session_token: str | None workspace_id: str | None _use_sigv4: bool _skip_auth: bool def __init__( self, *, api_key: str | None = None, aws_access_key: str | None = None, aws_secret_key: str | None = None, aws_region: str | None = None, aws_profile: str | None = None, aws_session_token: str | None = None, workspace_id: str | None = None, skip_auth: bool = False, base_url: str | httpx.URL | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, http_client: httpx.AsyncClient | None = None, middleware: Sequence[MiddlewareInput] | None = None, _strict_response_validation: bool = False, # Accepted for compatibility with AsyncAnthropic.copy() but not used auth_token: str | None = None, webhook_key: str | None = None, ) -> None: self._skip_auth = skip_auth validate_credentials(aws_access_key=aws_access_key, aws_secret_key=aws_secret_key) if skip_auth: self._use_sigv4 = False resolved_api_key = None else: self._use_sigv4 = resolve_auth_mode( api_key=api_key, aws_access_key=aws_access_key, aws_secret_key=aws_secret_key, aws_profile=aws_profile, ) resolved_api_key = resolve_api_key(api_key=api_key, use_sigv4=self._use_sigv4) resolved_region = resolve_region(aws_region) if self._use_sigv4 and resolved_region is None: raise AnthropicError( "No AWS region was provided. Set the `aws_region` argument or the `AWS_REGION`/`AWS_DEFAULT_REGION` environment variable." ) self.aws_access_key = aws_access_key self.aws_secret_key = aws_secret_key self.aws_region = resolved_region self.aws_profile = aws_profile self.aws_session_token = aws_session_token if skip_auth: self.workspace_id = workspace_id else: resolved_workspace_id = resolve_workspace_id(workspace_id) if resolved_workspace_id is None: raise AnthropicError( "No workspace ID found. Set the `workspace_id` argument or the `ANTHROPIC_AWS_WORKSPACE_ID` environment variable." ) self.workspace_id = resolved_workspace_id if not skip_auth: resolved_base_url = resolve_base_url( str(base_url) if base_url is not None else None, region=resolved_region, ) if resolved_base_url is None: raise AnthropicError( "No AWS region was provided and no base_url was given. " "Set the `aws_region` argument, the `AWS_REGION`/`AWS_DEFAULT_REGION` environment variable, " "or provide a `base_url` directly." ) base_url = resolved_base_url super().__init__( api_key=resolved_api_key, auth_token=auth_token, webhook_key=webhook_key, base_url=base_url, # type: ignore[arg-type] timeout=timeout, max_retries=max_retries, default_headers=default_headers, default_query=default_query, http_client=http_client, middleware=middleware, _strict_response_validation=_strict_response_validation, ) @property @override def default_headers(self) -> dict[str, str | Omit]: headers = {**super().default_headers} if self.workspace_id is not None: headers["anthropic-workspace-id"] = self.workspace_id return headers @property @override def _api_key_auth(self) -> dict[str, str]: if self._use_sigv4 or self._skip_auth: return {} return super()._api_key_auth @override def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: if self._use_sigv4 or self._skip_auth: return super()._validate_headers(headers, custom_headers) @override async def _prepare_request(self, request: httpx.Request) -> None: if not self._use_sigv4: return from ._auth import get_auth_headers data = request.read().decode() headers = get_auth_headers( method=request.method, url=str(request.url), headers=request.headers, aws_access_key=self.aws_access_key, aws_secret_key=self.aws_secret_key, aws_session_token=self.aws_session_token, region=self.aws_region, profile=self.aws_profile, data=data, service_name="aws-external-anthropic", ) request.headers.update(headers) @override def copy( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] — narrows `credentials` to None-only self, *, api_key: str | None = None, aws_access_key: str | None = None, aws_secret_key: str | None = None, aws_region: str | None = None, aws_profile: str | None = None, aws_session_token: str | None = None, workspace_id: str | None = None, skip_auth: bool | None = None, auth_token: str | None = None, credentials: AccessTokenProvider | None = None, webhook_key: str | None = None, base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, http_client: httpx.AsyncClient | None = None, max_retries: int | NotGiven = NOT_GIVEN, default_headers: Mapping[str, str] | None = None, set_default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, set_default_query: Mapping[str, object] | None = None, middleware: Sequence[MiddlewareInput] | None | NotGiven = NOT_GIVEN, _extra_kwargs: Mapping[str, Any] = {}, ) -> Self: # The AWS client authenticates with SigV4 (or an API key), not a token # provider, so it has no `credentials`. Accept the argument for signature # compatibility with the base client — internal helpers such as # `_copy_client_with_bearer_auth` call `copy(credentials=None, ...)` — but # only as a no-op; reject a real provider rather than silently ignoring it. if credentials is not None: raise TypeError("AnthropicAWS does not support a `credentials` provider (it authenticates with AWS SigV4).") # If region is changing and no explicit base_url, let __init__ derive it resolved_base_url = base_url or (None if aws_region else self.base_url) return super().copy( api_key=api_key or self.api_key, auth_token=auth_token, webhook_key=webhook_key, base_url=resolved_base_url, timeout=timeout, http_client=http_client, max_retries=max_retries, default_headers=default_headers, set_default_headers=set_default_headers, default_query=default_query, set_default_query=set_default_query, middleware=middleware, _extra_kwargs={ "aws_access_key": aws_access_key or self.aws_access_key, "aws_secret_key": aws_secret_key or self.aws_secret_key, "aws_region": aws_region or self.aws_region, "aws_profile": aws_profile or self.aws_profile, "aws_session_token": aws_session_token or self.aws_session_token, "workspace_id": workspace_id or self.workspace_id, "skip_auth": skip_auth if skip_auth is not None else self._skip_auth, **_extra_kwargs, }, ) with_options = copy # type: ignore[assignment] anthropic-sdk-python-0.120.2/src/anthropic/lib/aws/_credentials.py000066400000000000000000000072521523216435200250760ustar00rootroot00000000000000from __future__ import annotations import os from typing import Sequence def validate_credentials( *, aws_access_key: str | None, aws_secret_key: str | None, ) -> None: """Raise if only one of aws_access_key/aws_secret_key is provided.""" if (aws_access_key is not None) != (aws_secret_key is not None): provided = "aws_access_key" if aws_access_key is not None else "aws_secret_key" missing = "aws_secret_key" if aws_access_key is not None else "aws_access_key" raise ValueError( f"`{provided}` was provided without `{missing}`. " f"Both must be provided together, or neither (to use the default credential chain)." ) def _read_env(*env_vars: str) -> str | None: """Return the first non-None value from the given env vars, or None.""" for var in env_vars: value = os.environ.get(var) if value is not None: return value return None def resolve_auth_mode( *, api_key: str | None, aws_access_key: str | None, aws_secret_key: str | None, aws_profile: str | None, api_key_env_vars: Sequence[str] = ("ANTHROPIC_AWS_API_KEY",), ) -> bool: """Determine whether to use SigV4 auth. Returns True for SigV4, False for API key. Auth precedence: 1. api_key constructor arg → API key mode 2. aws_access_key + aws_secret_key constructor args → SigV4 3. aws_profile constructor arg → SigV4 4. API key env var(s) → API key mode (checked in order; first match wins) 5. Default AWS credential chain → SigV4 """ if api_key is not None: return False if aws_access_key is not None or aws_secret_key is not None: return True if aws_profile is not None: return True # No explicit constructor args that signal SigV4 — check env vars if _read_env(*api_key_env_vars) is not None: return False # Fall back to default AWS credential chain return True def resolve_api_key( *, api_key: str | None, use_sigv4: bool, api_key_env_vars: Sequence[str] = ("ANTHROPIC_AWS_API_KEY",), ) -> str | None: """Resolve the API key. Returns None if using SigV4.""" if api_key is not None: return api_key if not use_sigv4: # Must be from env var return _read_env(*api_key_env_vars) return None def resolve_region(aws_region: str | None) -> str | None: """Resolve the AWS region from constructor arg or env var. Does not silently default — returns None if no region is available. """ if aws_region is not None: return aws_region return os.environ.get("AWS_REGION") or os.environ.get("AWS_DEFAULT_REGION") def resolve_workspace_id( workspace_id: str | None, *, workspace_id_env_vars: Sequence[str] = ("ANTHROPIC_AWS_WORKSPACE_ID",), ) -> str | None: """Resolve the workspace ID from constructor arg or env var(s). Returns None if no workspace ID is available (caller should raise). """ if workspace_id is not None: return workspace_id return _read_env(*workspace_id_env_vars) def resolve_base_url( base_url: str | None, *, region: str | None, base_url_env_vars: Sequence[str] = ("ANTHROPIC_AWS_BASE_URL",), url_template: str = "https://aws-external-anthropic.{region}.api.aws", ) -> str | None: """Resolve the base URL from constructor arg, env var, or region. Returns None if no base URL is resolvable (caller should raise). """ if base_url is not None: return base_url env_url = _read_env(*base_url_env_vars) if env_url is not None: return env_url if region is not None: return url_template.format(region=region) return None anthropic-sdk-python-0.120.2/src/anthropic/lib/bedrock/000077500000000000000000000000001523216435200227015ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/lib/bedrock/__init__.py000066400000000000000000000003711523216435200250130ustar00rootroot00000000000000from ._client import AnthropicBedrock as AnthropicBedrock, AsyncAnthropicBedrock as AsyncAnthropicBedrock from ._mantle import ( AnthropicBedrockMantle as AnthropicBedrockMantle, AsyncAnthropicBedrockMantle as AsyncAnthropicBedrockMantle, ) anthropic-sdk-python-0.120.2/src/anthropic/lib/bedrock/_auth.py000066400000000000000000000035421523216435200243570ustar00rootroot00000000000000from __future__ import annotations from typing import TYPE_CHECKING import httpx from ..._utils import lru_cache if TYPE_CHECKING: import boto3 @lru_cache(maxsize=512) def _get_session( *, aws_access_key: str | None, aws_secret_key: str | None, aws_session_token: str | None, region: str | None, profile: str | None, ) -> boto3.Session: import boto3 return boto3.Session( profile_name=profile, region_name=region, aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_key, aws_session_token=aws_session_token, ) def get_auth_headers( *, method: str, url: str, headers: httpx.Headers, aws_access_key: str | None, aws_secret_key: str | None, aws_session_token: str | None, region: str | None, profile: str | None, data: str | None, ) -> dict[str, str]: from botocore.auth import SigV4Auth from botocore.awsrequest import AWSRequest session = _get_session( profile=profile, region=region, aws_access_key=aws_access_key, aws_secret_key=aws_secret_key, aws_session_token=aws_session_token, ) # The connection header may be stripped by a proxy somewhere, so the receiver # of this message may not see this header, so we remove it from the set of headers # that are signed. headers = headers.copy() del headers["connection"] request = AWSRequest(method=method.upper(), url=url, headers=headers, data=data) credentials = session.get_credentials() if not credentials: raise RuntimeError("could not resolve credentials from session") signer = SigV4Auth(credentials, "bedrock", session.region_name) signer.add_auth(request) prepped = request.prepare() return {key: value for key, value in dict(prepped.headers).items() if value is not None} anthropic-sdk-python-0.120.2/src/anthropic/lib/bedrock/_beta.py000066400000000000000000000063731523216435200243360ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ._beta_messages import ( Messages, AsyncMessages, MessagesWithRawResponse, AsyncMessagesWithRawResponse, MessagesWithStreamingResponse, AsyncMessagesWithStreamingResponse, ) __all__ = ["Beta", "AsyncBeta"] class Beta(SyncAPIResource): @cached_property def messages(self) -> Messages: return Messages(self._client) @cached_property def with_raw_response(self) -> BetaWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return BetaWithRawResponse(self) @cached_property def with_streaming_response(self) -> BetaWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return BetaWithStreamingResponse(self) class AsyncBeta(AsyncAPIResource): @cached_property def messages(self) -> AsyncMessages: return AsyncMessages(self._client) @cached_property def with_raw_response(self) -> AsyncBetaWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncBetaWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncBetaWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncBetaWithStreamingResponse(self) class BetaWithRawResponse: def __init__(self, beta: Beta) -> None: self._beta = beta @cached_property def messages(self) -> MessagesWithRawResponse: return MessagesWithRawResponse(self._beta.messages) class AsyncBetaWithRawResponse: def __init__(self, beta: AsyncBeta) -> None: self._beta = beta @cached_property def messages(self) -> AsyncMessagesWithRawResponse: return AsyncMessagesWithRawResponse(self._beta.messages) class BetaWithStreamingResponse: def __init__(self, beta: Beta) -> None: self._beta = beta @cached_property def messages(self) -> MessagesWithStreamingResponse: return MessagesWithStreamingResponse(self._beta.messages) class AsyncBetaWithStreamingResponse: def __init__(self, beta: AsyncBeta) -> None: self._beta = beta @cached_property def messages(self) -> AsyncMessagesWithStreamingResponse: return AsyncMessagesWithStreamingResponse(self._beta.messages) anthropic-sdk-python-0.120.2/src/anthropic/lib/bedrock/_beta_messages.py000066400000000000000000000061751523216435200262250ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from ... import _legacy_response from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ...resources.beta import Messages as FirstPartyMessagesAPI, AsyncMessages as FirstPartyAsyncMessagesAPI __all__ = ["Messages", "AsyncMessages"] class Messages(SyncAPIResource): create = FirstPartyMessagesAPI.create @cached_property def with_raw_response(self) -> MessagesWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return MessagesWithRawResponse(self) @cached_property def with_streaming_response(self) -> MessagesWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return MessagesWithStreamingResponse(self) class AsyncMessages(AsyncAPIResource): create = FirstPartyAsyncMessagesAPI.create @cached_property def with_raw_response(self) -> AsyncMessagesWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncMessagesWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncMessagesWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncMessagesWithStreamingResponse(self) class MessagesWithRawResponse: def __init__(self, messages: Messages) -> None: self._messages = messages self.create = _legacy_response.to_raw_response_wrapper( messages.create, ) class AsyncMessagesWithRawResponse: def __init__(self, messages: AsyncMessages) -> None: self._messages = messages self.create = _legacy_response.async_to_raw_response_wrapper( messages.create, ) class MessagesWithStreamingResponse: def __init__(self, messages: Messages) -> None: self._messages = messages self.create = to_streamed_response_wrapper( messages.create, ) class AsyncMessagesWithStreamingResponse: def __init__(self, messages: AsyncMessages) -> None: self._messages = messages self.create = async_to_streamed_response_wrapper( messages.create, ) anthropic-sdk-python-0.120.2/src/anthropic/lib/bedrock/_client.py000066400000000000000000000451151523216435200246760ustar00rootroot00000000000000from __future__ import annotations import os import logging import urllib.parse from typing import Any, Union, Mapping, TypeVar, Sequence from typing_extensions import Self, override import httpx from ... import _exceptions from ._beta import Beta, AsyncBeta from ..._types import NOT_GIVEN, Timeout, NotGiven from ..._utils import is_dict, is_given from ..._compat import model_copy from ..._version import __version__ from ..._streaming import Stream, AsyncStream from ..._exceptions import AnthropicError, APIStatusError from ..._middleware import MiddlewareInput from ..._base_client import ( DEFAULT_MAX_RETRIES, BaseClient, SyncAPIClient, AsyncAPIClient, FinalRequestOptions, merge_headers, ) from ._stream_decoder import AWSEventStreamDecoder from ...resources.messages import Messages, AsyncMessages from ...resources.completions import Completions, AsyncCompletions log: logging.Logger = logging.getLogger(__name__) DEFAULT_VERSION = "bedrock-2023-05-31" _HttpxClientT = TypeVar("_HttpxClientT", bound=Union[httpx.Client, httpx.AsyncClient]) _DefaultStreamT = TypeVar("_DefaultStreamT", bound=Union[Stream[Any], AsyncStream[Any]]) def _prepare_options(input_options: FinalRequestOptions) -> FinalRequestOptions: options = model_copy(input_options, deep=True) if is_dict(options.json_data): options.json_data.setdefault("anthropic_version", DEFAULT_VERSION) if is_given(options.headers): betas = options.headers.get("anthropic-beta") if betas: options.json_data.setdefault("anthropic_beta", betas.split(",")) if options.url in {"/v1/complete", "/v1/messages", "/v1/messages?beta=true"} and options.method == "post": if not is_dict(options.json_data): raise RuntimeError("Expected dictionary json_data for post /completions endpoint") model = options.json_data.pop("model", None) model = urllib.parse.quote(str(model), safe=":") stream = options.json_data.pop("stream", False) if stream: options.url = f"/model/{model}/invoke-with-response-stream" else: options.url = f"/model/{model}/invoke" if options.url.startswith("/v1/messages/batches"): raise AnthropicError("The Batch API is not supported in Bedrock yet") if options.url == "/v1/messages/count_tokens": raise AnthropicError("Token counting is not supported in Bedrock yet") return options def _infer_region() -> str: """ Infer the AWS region from the environment variables or from the boto3 session if available. """ aws_region = os.environ.get("AWS_REGION") if aws_region is None: try: import boto3 session = boto3.Session() if session.region_name: aws_region = session.region_name except ImportError: pass if aws_region is None: log.warning("No AWS region specified, defaulting to us-east-1") aws_region = "us-east-1" # fall back to legacy behavior return aws_region class BaseBedrockClient(BaseClient[_HttpxClientT, _DefaultStreamT]): @override def _make_status_error( self, err_msg: str, *, body: object, response: httpx.Response, ) -> APIStatusError: if response.status_code == 400: return _exceptions.BadRequestError(err_msg, response=response, body=body) if response.status_code == 401: return _exceptions.AuthenticationError(err_msg, response=response, body=body) if response.status_code == 403: return _exceptions.PermissionDeniedError(err_msg, response=response, body=body) if response.status_code == 404: return _exceptions.NotFoundError(err_msg, response=response, body=body) if response.status_code == 409: return _exceptions.ConflictError(err_msg, response=response, body=body) if response.status_code == 422: return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body) if response.status_code == 429: return _exceptions.RateLimitError(err_msg, response=response, body=body) if response.status_code == 503: return _exceptions.ServiceUnavailableError(err_msg, response=response, body=body) if response.status_code >= 500: return _exceptions.InternalServerError(err_msg, response=response, body=body) return APIStatusError(err_msg, response=response, body=body) class AnthropicBedrock(BaseBedrockClient[httpx.Client, Stream[Any]], SyncAPIClient): messages: Messages completions: Completions beta: Beta def __init__( self, aws_secret_key: str | None = None, aws_access_key: str | None = None, aws_region: str | None = None, aws_profile: str | None = None, aws_session_token: str | None = None, api_key: str | None = None, base_url: str | httpx.URL | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, # Configure a custom httpx client. See the [httpx documentation](https://www.python-httpx.org/api/#client) for more details. http_client: httpx.Client | None = None, middleware: Sequence[MiddlewareInput] | None = None, # Enable or disable schema validation for data returned by the API. # When enabled an error APIResponseValidationError is raised # if the API responds with invalid data for the expected schema. # # This parameter may be removed or changed in the future. # If you rely on this feature, please open a GitHub issue # outlining your use-case to help us decide if it should be # part of our public interface in the future. _strict_response_validation: bool = False, ) -> None: if api_key is None: api_key = os.environ.get("AWS_BEARER_TOKEN_BEDROCK") has_aws_credentials = ( aws_access_key is not None or aws_secret_key is not None or aws_session_token is not None or aws_profile is not None ) if api_key is not None and has_aws_credentials: raise ValueError( "Cannot specify both `api_key` and AWS credentials (`aws_access_key`, `aws_secret_key`, `aws_session_token`, `aws_profile`)" ) self.api_key: str | None = api_key self.aws_secret_key = aws_secret_key self.aws_access_key = aws_access_key self.aws_region = _infer_region() if aws_region is None else aws_region self.aws_profile = aws_profile self.aws_session_token = aws_session_token if base_url is None: base_url = os.environ.get("ANTHROPIC_BEDROCK_BASE_URL") if base_url is None: base_url = f"https://bedrock-runtime.{self.aws_region}.amazonaws.com" super().__init__( version=__version__, base_url=base_url, timeout=timeout, max_retries=max_retries, custom_headers=default_headers, custom_query=default_query, http_client=http_client, middleware=middleware, _strict_response_validation=_strict_response_validation, ) self.beta = Beta(self) self.messages = Messages(self) self.completions = Completions(self) @override def _make_sse_decoder(self) -> AWSEventStreamDecoder: return AWSEventStreamDecoder() @override def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: return _prepare_options(options) @override def _prepare_request(self, request: httpx.Request) -> None: if self.api_key is not None: request.headers["Authorization"] = f"Bearer {self.api_key}" return from ._auth import get_auth_headers data = request.read().decode() headers = get_auth_headers( method=request.method, url=str(request.url), headers=request.headers, aws_access_key=self.aws_access_key, aws_secret_key=self.aws_secret_key, aws_session_token=self.aws_session_token, region=self.aws_region or "us-east-1", profile=self.aws_profile, data=data, ) request.headers.update(headers) def copy( self, *, aws_secret_key: str | None = None, aws_access_key: str | None = None, aws_region: str | None = None, aws_session_token: str | None = None, api_key: str | None = None, base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, http_client: httpx.Client | None = None, max_retries: int | NotGiven = NOT_GIVEN, default_headers: Mapping[str, str] | None = None, set_default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, set_default_query: Mapping[str, object] | None = None, middleware: Sequence[MiddlewareInput] | None | NotGiven = NOT_GIVEN, _extra_kwargs: Mapping[str, Any] = {}, ) -> Self: """ Create a new client instance re-using the same options given to the current client with optional overriding. """ if default_headers is not None and set_default_headers is not None: raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") if default_query is not None and set_default_query is not None: raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") headers = self._custom_headers if default_headers is not None: headers = merge_headers(headers, default_headers) elif set_default_headers is not None: headers = set_default_headers params = self._custom_query if default_query is not None: params = {**params, **default_query} elif set_default_query is not None: params = set_default_query return self.__class__( aws_secret_key=aws_secret_key or self.aws_secret_key, aws_access_key=aws_access_key or self.aws_access_key, aws_region=aws_region or self.aws_region, aws_session_token=aws_session_token or self.aws_session_token, api_key=api_key or self.api_key, base_url=base_url or self.base_url, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client, max_retries=max_retries if is_given(max_retries) else self.max_retries, default_headers=headers, default_query=params, middleware=self._middleware if isinstance(middleware, NotGiven) else middleware, **_extra_kwargs, ) # Alias for `copy` for nicer inline usage, e.g. # client.with_options(timeout=10).foo.create(...) with_options = copy def with_middleware(self, *middleware: MiddlewareInput) -> Self: """A new client with the given middleware appended after this client's middleware. Convenience for applying extra middleware to a single request: ```py client.with_middleware(my_middleware).messages.create(...) ``` """ return self.copy(middleware=[*self._middleware, *middleware]) class AsyncAnthropicBedrock(BaseBedrockClient[httpx.AsyncClient, AsyncStream[Any]], AsyncAPIClient): messages: AsyncMessages completions: AsyncCompletions beta: AsyncBeta def __init__( self, aws_secret_key: str | None = None, aws_access_key: str | None = None, aws_region: str | None = None, aws_profile: str | None = None, aws_session_token: str | None = None, api_key: str | None = None, base_url: str | httpx.URL | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, # Configure a custom httpx client. See the [httpx documentation](https://www.python-httpx.org/api/#client) for more details. http_client: httpx.AsyncClient | None = None, middleware: Sequence[MiddlewareInput] | None = None, # Enable or disable schema validation for data returned by the API. # When enabled an error APIResponseValidationError is raised # if the API responds with invalid data for the expected schema. # # This parameter may be removed or changed in the future. # If you rely on this feature, please open a GitHub issue # outlining your use-case to help us decide if it should be # part of our public interface in the future. _strict_response_validation: bool = False, ) -> None: if api_key is None: api_key = os.environ.get("AWS_BEARER_TOKEN_BEDROCK") has_aws_credentials = ( aws_access_key is not None or aws_secret_key is not None or aws_session_token is not None or aws_profile is not None ) if api_key is not None and has_aws_credentials: raise ValueError( "Cannot specify both `api_key` and AWS credentials (`aws_access_key`, `aws_secret_key`, `aws_session_token`, `aws_profile`)" ) self.api_key: str | None = api_key self.aws_secret_key = aws_secret_key self.aws_access_key = aws_access_key self.aws_region = _infer_region() if aws_region is None else aws_region self.aws_profile = aws_profile self.aws_session_token = aws_session_token if base_url is None: base_url = os.environ.get("ANTHROPIC_BEDROCK_BASE_URL") if base_url is None: base_url = f"https://bedrock-runtime.{self.aws_region}.amazonaws.com" super().__init__( version=__version__, base_url=base_url, timeout=timeout, max_retries=max_retries, custom_headers=default_headers, custom_query=default_query, http_client=http_client, middleware=middleware, _strict_response_validation=_strict_response_validation, ) self.messages = AsyncMessages(self) self.completions = AsyncCompletions(self) self.beta = AsyncBeta(self) @override def _make_sse_decoder(self) -> AWSEventStreamDecoder: return AWSEventStreamDecoder() @override async def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: return _prepare_options(options) @override async def _prepare_request(self, request: httpx.Request) -> None: if self.api_key is not None: request.headers["Authorization"] = f"Bearer {self.api_key}" return from ._auth import get_auth_headers data = request.read().decode() headers = get_auth_headers( method=request.method, url=str(request.url), headers=request.headers, aws_access_key=self.aws_access_key, aws_secret_key=self.aws_secret_key, aws_session_token=self.aws_session_token, region=self.aws_region or "us-east-1", profile=self.aws_profile, data=data, ) request.headers.update(headers) def copy( self, *, aws_secret_key: str | None = None, aws_access_key: str | None = None, aws_region: str | None = None, aws_session_token: str | None = None, api_key: str | None = None, base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, http_client: httpx.AsyncClient | None = None, max_retries: int | NotGiven = NOT_GIVEN, default_headers: Mapping[str, str] | None = None, set_default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, set_default_query: Mapping[str, object] | None = None, middleware: Sequence[MiddlewareInput] | None | NotGiven = NOT_GIVEN, _extra_kwargs: Mapping[str, Any] = {}, ) -> Self: """ Create a new client instance re-using the same options given to the current client with optional overriding. """ if default_headers is not None and set_default_headers is not None: raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") if default_query is not None and set_default_query is not None: raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") headers = self._custom_headers if default_headers is not None: headers = merge_headers(headers, default_headers) elif set_default_headers is not None: headers = set_default_headers params = self._custom_query if default_query is not None: params = {**params, **default_query} elif set_default_query is not None: params = set_default_query return self.__class__( aws_secret_key=aws_secret_key or self.aws_secret_key, aws_access_key=aws_access_key or self.aws_access_key, aws_region=aws_region or self.aws_region, aws_session_token=aws_session_token or self.aws_session_token, api_key=api_key or self.api_key, base_url=base_url or self.base_url, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client, max_retries=max_retries if is_given(max_retries) else self.max_retries, default_headers=headers, default_query=params, middleware=self._middleware if isinstance(middleware, NotGiven) else middleware, **_extra_kwargs, ) # Alias for `copy` for nicer inline usage, e.g. # client.with_options(timeout=10).foo.create(...) with_options = copy def with_middleware(self, *middleware: MiddlewareInput) -> Self: """A new client with the given middleware appended after this client's middleware. Convenience for applying extra middleware to a single request: ```py client.with_middleware(my_middleware).messages.create(...) ``` """ return self.copy(middleware=[*self._middleware, *middleware]) anthropic-sdk-python-0.120.2/src/anthropic/lib/bedrock/_mantle.py000066400000000000000000000457441523216435200247100ustar00rootroot00000000000000from __future__ import annotations import os from typing import Any, Union, Mapping, TypeVar, Sequence from typing_extensions import Self, override import httpx from ... import _exceptions from ..._qs import Querystring from ..._types import NOT_GIVEN, Omit, Timeout, NotGiven from ..._utils import is_given from ..._compat import cached_property from ..._version import __version__ from ..aws._auth import get_auth_headers from ..._resource import SyncAPIResource, AsyncAPIResource from ..._streaming import Stream, AsyncStream from ..._exceptions import AnthropicError, APIStatusError from ..._middleware import MiddlewareInput from ..._base_client import ( DEFAULT_MAX_RETRIES, BaseClient, SyncAPIClient, AsyncAPIClient, merge_headers, ) from ..aws._credentials import ( resolve_region, resolve_api_key, resolve_auth_mode, validate_credentials, ) from ...resources.messages import Messages, AsyncMessages from ...resources.beta.messages import Messages as BetaMessages, AsyncMessages as AsyncBetaMessages DEFAULT_SERVICE_NAME = "bedrock-mantle" _MANTLE_API_KEY_ENV_VARS = ("AWS_BEARER_TOKEN_BEDROCK", "ANTHROPIC_AWS_API_KEY") _HttpxClientT = TypeVar("_HttpxClientT", bound=Union[httpx.Client, httpx.AsyncClient]) _DefaultStreamT = TypeVar("_DefaultStreamT", bound=Union[Stream[Any], AsyncStream[Any]]) # --- Beta resources (messages-only) --- class MantleBeta(SyncAPIResource): @cached_property def messages(self) -> BetaMessages: return BetaMessages(self._client) class AsyncMantleBeta(AsyncAPIResource): @cached_property def messages(self) -> AsyncBetaMessages: return AsyncBetaMessages(self._client) # --- Base --- class BaseMantleClient(BaseClient[_HttpxClientT, _DefaultStreamT]): @override def _make_status_error( self, err_msg: str, *, body: object, response: httpx.Response, ) -> APIStatusError: if response.status_code == 400: return _exceptions.BadRequestError(err_msg, response=response, body=body) if response.status_code == 401: return _exceptions.AuthenticationError(err_msg, response=response, body=body) if response.status_code == 403: return _exceptions.PermissionDeniedError(err_msg, response=response, body=body) if response.status_code == 404: return _exceptions.NotFoundError(err_msg, response=response, body=body) if response.status_code == 409: return _exceptions.ConflictError(err_msg, response=response, body=body) if response.status_code == 413: return _exceptions.RequestTooLargeError(err_msg, response=response, body=body) if response.status_code == 422: return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body) if response.status_code == 429: return _exceptions.RateLimitError(err_msg, response=response, body=body) if response.status_code == 529: return _exceptions.OverloadedError(err_msg, response=response, body=body) if response.status_code >= 500: return _exceptions.InternalServerError(err_msg, response=response, body=body) return APIStatusError(err_msg, response=response, body=body) # --- Shared init logic --- def _resolve_mantle_config( *, api_key: str | None, aws_access_key: str | None, aws_secret_key: str | None, aws_region: str | None, aws_profile: str | None, skip_auth: bool, base_url: str | httpx.URL | None, default_headers: Mapping[str, str] | None, ) -> tuple[str | None, str | httpx.URL, bool, dict[str, str]]: """Resolve and validate all Mantle client configuration. Returns (resolved_api_key, resolved_base_url, use_sigv4, merged_headers). """ if skip_auth: use_sigv4 = False resolved_api_key = None else: validate_credentials(aws_access_key=aws_access_key, aws_secret_key=aws_secret_key) use_sigv4 = resolve_auth_mode( api_key=api_key, aws_access_key=aws_access_key, aws_secret_key=aws_secret_key, aws_profile=aws_profile, api_key_env_vars=_MANTLE_API_KEY_ENV_VARS, ) resolved_api_key = resolve_api_key( api_key=api_key, use_sigv4=use_sigv4, api_key_env_vars=_MANTLE_API_KEY_ENV_VARS, ) resolved_region = resolve_region(aws_region) if base_url is None: base_url = os.environ.get("ANTHROPIC_BEDROCK_MANTLE_BASE_URL") if base_url is None: if resolved_region is None: raise AnthropicError( "No AWS region or base URL found. Set `aws_region` in the constructor, " "the `AWS_REGION` / `AWS_DEFAULT_REGION` environment variable, or provide " "a `base_url` / `ANTHROPIC_BEDROCK_MANTLE_BASE_URL` environment variable." ) base_url = f"https://bedrock-mantle.{resolved_region}.api.aws/anthropic" merged_headers: dict[str, str] = {} if default_headers: merged_headers.update(default_headers) return resolved_api_key, base_url, use_sigv4, merged_headers # --- Sync client --- class AnthropicBedrockMantle(BaseMantleClient[httpx.Client, Stream[Any]], SyncAPIClient): messages: Messages beta: MantleBeta aws_region: str | None aws_access_key: str | None aws_secret_key: str | None aws_session_token: str | None aws_profile: str | None skip_auth: bool _use_sigv4: bool def __init__( self, *, aws_access_key: str | None = None, aws_secret_key: str | None = None, aws_session_token: str | None = None, aws_region: str | None = None, aws_profile: str | None = None, api_key: str | None = None, skip_auth: bool = False, base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, http_client: httpx.Client | None = None, middleware: Sequence[MiddlewareInput] | None = None, _strict_response_validation: bool = False, ) -> None: resolved_api_key, resolved_base_url, use_sigv4, merged_headers = _resolve_mantle_config( api_key=api_key, aws_access_key=aws_access_key, aws_secret_key=aws_secret_key, aws_region=aws_region, aws_profile=aws_profile, skip_auth=skip_auth, base_url=base_url, default_headers=default_headers, ) resolved_region = resolve_region(aws_region) super().__init__( version=__version__, base_url=resolved_base_url, timeout=timeout, max_retries=max_retries, custom_headers=merged_headers, custom_query=default_query, http_client=http_client, middleware=middleware, _strict_response_validation=_strict_response_validation, ) self.api_key = resolved_api_key self.aws_region = resolved_region self.aws_access_key = aws_access_key self.aws_secret_key = aws_secret_key self.aws_session_token = aws_session_token self.aws_profile = aws_profile self.skip_auth = skip_auth self._use_sigv4 = use_sigv4 self.messages = Messages(self) self.beta = MantleBeta(self) @property @override def qs(self) -> Querystring: return Querystring(array_format="comma") @property @override def auth_headers(self) -> dict[str, str]: if self.skip_auth or self._use_sigv4: return {} api_key = self.api_key if api_key is None: return {} return {"Authorization": f"Bearer {api_key}"} @property @override def default_headers(self) -> dict[str, str | Omit]: return { **super().default_headers, "X-Stainless-Async": "false", "anthropic-version": "2023-06-01", **self._custom_headers, } @override def _validate_headers(self, headers: Any, custom_headers: Any) -> None: pass @override def _prepare_request(self, request: httpx.Request) -> None: if self.skip_auth or not self._use_sigv4: return data = request.read().decode() headers = get_auth_headers( method=request.method, url=str(request.url), headers=request.headers, aws_access_key=self.aws_access_key, aws_secret_key=self.aws_secret_key, aws_session_token=self.aws_session_token, region=self.aws_region, profile=self.aws_profile, data=data, service_name=DEFAULT_SERVICE_NAME, ) request.headers.update(headers) def copy( self, *, api_key: str | None = None, aws_access_key: str | None = None, aws_secret_key: str | None = None, aws_session_token: str | None = None, aws_region: str | None = None, aws_profile: str | None = None, skip_auth: bool | None = None, base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, http_client: httpx.Client | None = None, max_retries: int | NotGiven = NOT_GIVEN, default_headers: Mapping[str, str] | None = None, set_default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, set_default_query: Mapping[str, object] | None = None, middleware: Sequence[MiddlewareInput] | None | NotGiven = NOT_GIVEN, _extra_kwargs: Mapping[str, Any] = {}, ) -> Self: """ Create a new client instance re-using the same options given to the current client with optional overriding. """ if default_headers is not None and set_default_headers is not None: raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") if default_query is not None and set_default_query is not None: raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") headers = self._custom_headers if default_headers is not None: headers = merge_headers(headers, default_headers) elif set_default_headers is not None: headers = set_default_headers params = self._custom_query if default_query is not None: params = {**params, **default_query} elif set_default_query is not None: params = set_default_query return self.__class__( api_key=api_key or self.api_key, aws_access_key=aws_access_key or self.aws_access_key, aws_secret_key=aws_secret_key or self.aws_secret_key, aws_session_token=aws_session_token or self.aws_session_token, aws_region=aws_region or self.aws_region, aws_profile=aws_profile or self.aws_profile, skip_auth=skip_auth if skip_auth is not None else self.skip_auth, base_url=base_url or self.base_url, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client, max_retries=max_retries if is_given(max_retries) else self.max_retries, default_headers=headers, default_query=params, middleware=self._middleware if isinstance(middleware, NotGiven) else middleware, **_extra_kwargs, ) with_options = copy def with_middleware(self, *middleware: MiddlewareInput) -> Self: """A new client with the given middleware appended after this client's middleware. Convenience for applying extra middleware to a single request: ```py client.with_middleware(my_middleware).messages.create(...) ``` """ return self.copy(middleware=[*self._middleware, *middleware]) # --- Async client --- class AsyncAnthropicBedrockMantle(BaseMantleClient[httpx.AsyncClient, AsyncStream[Any]], AsyncAPIClient): messages: AsyncMessages beta: AsyncMantleBeta aws_region: str | None aws_access_key: str | None aws_secret_key: str | None aws_session_token: str | None aws_profile: str | None skip_auth: bool _use_sigv4: bool def __init__( self, *, aws_access_key: str | None = None, aws_secret_key: str | None = None, aws_session_token: str | None = None, aws_region: str | None = None, aws_profile: str | None = None, api_key: str | None = None, skip_auth: bool = False, base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, http_client: httpx.AsyncClient | None = None, middleware: Sequence[MiddlewareInput] | None = None, _strict_response_validation: bool = False, ) -> None: resolved_api_key, resolved_base_url, use_sigv4, merged_headers = _resolve_mantle_config( api_key=api_key, aws_access_key=aws_access_key, aws_secret_key=aws_secret_key, aws_region=aws_region, aws_profile=aws_profile, skip_auth=skip_auth, base_url=base_url, default_headers=default_headers, ) resolved_region = resolve_region(aws_region) super().__init__( version=__version__, base_url=resolved_base_url, timeout=timeout, max_retries=max_retries, custom_headers=merged_headers, custom_query=default_query, http_client=http_client, middleware=middleware, _strict_response_validation=_strict_response_validation, ) self.api_key = resolved_api_key self.aws_region = resolved_region self.aws_access_key = aws_access_key self.aws_secret_key = aws_secret_key self.aws_session_token = aws_session_token self.aws_profile = aws_profile self.skip_auth = skip_auth self._use_sigv4 = use_sigv4 self.messages = AsyncMessages(self) self.beta = AsyncMantleBeta(self) @property @override def qs(self) -> Querystring: return Querystring(array_format="comma") @property @override def auth_headers(self) -> dict[str, str]: if self.skip_auth or self._use_sigv4: return {} api_key = self.api_key if api_key is None: return {} return {"Authorization": f"Bearer {api_key}"} @property @override def default_headers(self) -> dict[str, str | Omit]: return { **super().default_headers, "X-Stainless-Async": "async:asyncio", "anthropic-version": "2023-06-01", **self._custom_headers, } @override def _validate_headers(self, headers: Any, custom_headers: Any) -> None: pass @override async def _prepare_request(self, request: httpx.Request) -> None: if self.skip_auth or not self._use_sigv4: return data = request.read().decode() headers = get_auth_headers( method=request.method, url=str(request.url), headers=request.headers, aws_access_key=self.aws_access_key, aws_secret_key=self.aws_secret_key, aws_session_token=self.aws_session_token, region=self.aws_region, profile=self.aws_profile, data=data, service_name=DEFAULT_SERVICE_NAME, ) request.headers.update(headers) def copy( self, *, api_key: str | None = None, aws_access_key: str | None = None, aws_secret_key: str | None = None, aws_session_token: str | None = None, aws_region: str | None = None, aws_profile: str | None = None, skip_auth: bool | None = None, base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, http_client: httpx.AsyncClient | None = None, max_retries: int | NotGiven = NOT_GIVEN, default_headers: Mapping[str, str] | None = None, set_default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, set_default_query: Mapping[str, object] | None = None, middleware: Sequence[MiddlewareInput] | None | NotGiven = NOT_GIVEN, _extra_kwargs: Mapping[str, Any] = {}, ) -> Self: """ Create a new client instance re-using the same options given to the current client with optional overriding. """ if default_headers is not None and set_default_headers is not None: raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") if default_query is not None and set_default_query is not None: raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") headers = self._custom_headers if default_headers is not None: headers = merge_headers(headers, default_headers) elif set_default_headers is not None: headers = set_default_headers params = self._custom_query if default_query is not None: params = {**params, **default_query} elif set_default_query is not None: params = set_default_query return self.__class__( api_key=api_key or self.api_key, aws_access_key=aws_access_key or self.aws_access_key, aws_secret_key=aws_secret_key or self.aws_secret_key, aws_session_token=aws_session_token or self.aws_session_token, aws_region=aws_region or self.aws_region, aws_profile=aws_profile or self.aws_profile, skip_auth=skip_auth if skip_auth is not None else self.skip_auth, base_url=base_url or self.base_url, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client, max_retries=max_retries if is_given(max_retries) else self.max_retries, default_headers=headers, default_query=params, middleware=self._middleware if isinstance(middleware, NotGiven) else middleware, **_extra_kwargs, ) with_options = copy def with_middleware(self, *middleware: MiddlewareInput) -> Self: """A new client with the given middleware appended after this client's middleware. Convenience for applying extra middleware to a single request: ```py client.with_middleware(my_middleware).messages.create(...) ``` """ return self.copy(middleware=[*self._middleware, *middleware]) anthropic-sdk-python-0.120.2/src/anthropic/lib/bedrock/_stream.py000066400000000000000000000015471523216435200247140ustar00rootroot00000000000000from __future__ import annotations from typing import TypeVar import httpx from ..._client import Anthropic, AsyncAnthropic from ..._streaming import Stream, AsyncStream from ._stream_decoder import AWSEventStreamDecoder _T = TypeVar("_T") class BedrockStream(Stream[_T]): def __init__( self, *, cast_to: type[_T], response: httpx.Response, client: Anthropic, ) -> None: super().__init__(cast_to=cast_to, response=response, client=client) self._decoder = AWSEventStreamDecoder() class AsyncBedrockStream(AsyncStream[_T]): def __init__( self, *, cast_to: type[_T], response: httpx.Response, client: AsyncAnthropic, ) -> None: super().__init__(cast_to=cast_to, response=response, client=client) self._decoder = AWSEventStreamDecoder() anthropic-sdk-python-0.120.2/src/anthropic/lib/bedrock/_stream_decoder.py000066400000000000000000000056351523216435200264030ustar00rootroot00000000000000from __future__ import annotations import json from typing import TYPE_CHECKING, Any, Dict, Iterator, AsyncIterator, cast from ..._utils import lru_cache from ..._streaming import ServerSentEvent if TYPE_CHECKING: from botocore.model import Shape from botocore.eventstream import EventStreamMessage @lru_cache(maxsize=None) def get_response_stream_shape() -> Shape: from botocore.model import ServiceModel from botocore.loaders import Loader loader = Loader() bedrock_service_dict = loader.load_service_model("bedrock-runtime", "service-2") bedrock_service_model = ServiceModel(bedrock_service_dict) return bedrock_service_model.shape_for("ResponseStream") class AWSEventStreamDecoder: def __init__(self) -> None: from botocore.parsers import EventStreamJSONParser self.parser = EventStreamJSONParser() def iter_bytes(self, iterator: Iterator[bytes]) -> Iterator[ServerSentEvent]: """Given an iterator that yields lines, iterate over it & yield every event encountered""" from botocore.eventstream import EventStreamBuffer event_stream_buffer = EventStreamBuffer() for chunk in iterator: event_stream_buffer.add_data(chunk) for event in event_stream_buffer: sse = self._parse_message_from_event(event) if sse: yield sse async def aiter_bytes(self, iterator: AsyncIterator[bytes]) -> AsyncIterator[ServerSentEvent]: """Given an async iterator that yields lines, iterate over it & yield every event encountered""" from botocore.eventstream import EventStreamBuffer event_stream_buffer = EventStreamBuffer() async for chunk in iterator: event_stream_buffer.add_data(chunk) for event in event_stream_buffer: sse = self._parse_message_from_event(event) if sse: yield sse def _parse_message_from_event(self, event: EventStreamMessage) -> ServerSentEvent | None: response_dict = event.to_response_dict() parsed_response = self.parser.parse(response_dict, get_response_stream_shape()) if response_dict["status_code"] != 200: raise ValueError(f"Bad response code, expected 200: {response_dict}") chunk = parsed_response.get("chunk") if not chunk: return None return _chunk_bytes_to_sse(chunk.get("bytes")) def _chunk_bytes_to_sse(raw: bytes) -> ServerSentEvent | None: decoded = raw.decode() data: Any try: data = json.loads(decoded) except Exception: data = None if not isinstance(data, dict): return ServerSentEvent(data=decoded, event="completion") payload = cast("Dict[str, Any]", data) event_type = payload.get("type") if not isinstance(event_type, str): event_type = "completion" return ServerSentEvent(data=decoded, event=event_type) anthropic-sdk-python-0.120.2/src/anthropic/lib/credentials/000077500000000000000000000000001523216435200235655ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/lib/credentials/__init__.py000066400000000000000000000022171523216435200257000ustar00rootroot00000000000000from ._auth import AccessTokenAuth as AccessTokenAuth from ._cache import TokenCache as TokenCache from ._chain import default_credentials as default_credentials from ._types import ( AccessToken as AccessToken, CredentialResult as CredentialResult, AccessTokenProvider as AccessTokenProvider, IdentityTokenProvider as IdentityTokenProvider, ) from ._workload import ( WorkloadIdentityError as WorkloadIdentityError, WorkloadIdentityCredentials as WorkloadIdentityCredentials, exchange_federation_assertion as exchange_federation_assertion, ) from ._providers import ( EnvToken as EnvToken, StaticToken as StaticToken, InMemoryConfig as InMemoryConfig, CredentialsFile as CredentialsFile, IdentityTokenFile as IdentityTokenFile, ) __all__ = [ "AccessToken", "AccessTokenProvider", "CredentialResult", "IdentityTokenProvider", "StaticToken", "EnvToken", "CredentialsFile", "InMemoryConfig", "IdentityTokenFile", "WorkloadIdentityCredentials", "WorkloadIdentityError", "exchange_federation_assertion", "TokenCache", "AccessTokenAuth", "default_credentials", ] anthropic-sdk-python-0.120.2/src/anthropic/lib/credentials/_auth.py000066400000000000000000000116641523216435200252470ustar00rootroot00000000000000from __future__ import annotations import logging import threading from typing import Generator, AsyncGenerator from typing_extensions import override import httpx from ._cache import TokenCache from ..._utils import asyncify from ._constants import OAUTH_API_BETA_HEADER __all__ = ["AccessTokenAuth"] log: logging.Logger = logging.getLogger(__name__) _warn_once_lock = threading.Lock() _warn_once_seen: set[str] = set() def _warn_once(key: str, message: str, *args: object) -> None: """Emit a log warning at most once per ``key`` per process.""" with _warn_once_lock: if key in _warn_once_seen: return _warn_once_seen.add(key) log.warning(message, *args) def warn_explicit_static_shadows_credentials(param: str) -> None: """Warn that an explicit ``api_key=`` / ``auth_token=`` argument shadows an explicit ``credentials=`` provider passed to the same constructor or ``copy()`` call. The static credential wins at the request-header level (``AccessTokenAuth.sync_auth_flow`` short-circuits on the pre-set header), which silently disables the credentials provider. """ _warn_once( f"explicit-shadow:{param}", "`%s=` was passed alongside `credentials=`; the static credential " "takes precedence and the credentials provider is silently disabled. " "Pass only one.", param, ) def warn_env_static_shadows_auto_discovery(env_var: str) -> None: """Warn that an ``ANTHROPIC_API_KEY`` / ``ANTHROPIC_AUTH_TOKEN`` from the environment is shadowing the SDK's profile / federation auto-discovery. Per the credential-precedence spec, a static-credential env var silently disables the auto-discovered federation and profile paths. Surface a one-shot warning so migrating users can see why their ``ANTHROPIC_PROFILE`` or WIF env vars are being ignored. """ _warn_once( f"env-shadow:{env_var}", "%s is set and takes precedence over the SDK's profile / federation " "auto-discovery; unset %s to use the auto-discovered credential.", env_var, env_var, ) class AccessTokenAuth(httpx.Auth): """Adapts a :class:`TokenCache` to httpx's :class:`~httpx.Auth` protocol. Used by :meth:`anthropic.Anthropic.custom_auth` to inject ``Authorization: Bearer`` plus the OAuth beta header on every request, with proactive refresh handled by :class:`TokenCache`. Static credentials shadow federation: if the outgoing request already carries an ``X-Api-Key`` or ``Authorization`` header (set by the client's api_key / auth_token path), this auth flow is a no-op. That matches the Go SDK's ``authMiddleware`` and the documented precedence in the WIF user guide — a static ``ANTHROPIC_API_KEY`` shadows any credentials provider. """ requires_response_body = False def __init__(self, token_cache: TokenCache) -> None: self._token_cache = token_cache @staticmethod def _has_static_credential(request: httpx.Request) -> bool: return bool(request.headers.get("X-Api-Key") or request.headers.get("Authorization")) def _apply(self, request: httpx.Request, token: str) -> None: request.headers["Authorization"] = f"Bearer {token}" existing_beta = request.headers.get("anthropic-beta", "") # Tokenize the comma-separated header so dedupe matches whole flag # names rather than substrings — `oauth-2025-04-20` would otherwise # spuriously match a future `oauth-2025-04-20b`. # # The flag we inject here is the *API* beta (oauth-2025-04-20), which # unlocks `Authorization: Bearer` auth on the API. The *federation* # beta (oidc-federation-2026-04-01) is a separate routing switch used # only on jwt-bearer POSTs to /v1/oauth/token — see _workload.py. existing_flags = [flag.strip() for flag in existing_beta.split(",") if flag.strip()] if OAUTH_API_BETA_HEADER not in existing_flags: existing_flags.append(OAUTH_API_BETA_HEADER) request.headers["anthropic-beta"] = ", ".join(existing_flags) @override def sync_auth_flow(self, request: httpx.Request) -> Generator[httpx.Request, httpx.Response, None]: if self._has_static_credential(request): yield request return token = self._token_cache.get_token() self._apply(request, token) yield request @override async def async_auth_flow(self, request: httpx.Request) -> AsyncGenerator[httpx.Request, httpx.Response]: if self._has_static_credential(request): yield request return # TokenCache.get_token is sync (and may make a blocking HTTP call); run it # in a worker thread to avoid blocking the event loop. Uses the same # ``asyncify`` helper as the rest of the SDK (see lib/vertex). token = await asyncify(self._token_cache.get_token)() self._apply(request, token) yield request anthropic-sdk-python-0.120.2/src/anthropic/lib/credentials/_cache.py000066400000000000000000000202141523216435200253400ustar00rootroot00000000000000from __future__ import annotations import time import logging import threading from typing import Callable, Optional import httpx from ._types import AccessToken, AccessTokenProvider from ._workload import WorkloadIdentityError from ._constants import ADVISORY_REFRESH_SECONDS, MANDATORY_REFRESH_SECONDS from ..._exceptions import AnthropicError __all__ = ["TokenCache"] log: logging.Logger = logging.getLogger(__name__) # Skip advisory refreshes for this many seconds after a failure so a # token-endpoint outage isn't hammered at request rate. Fixed (no jitter): # trades fast recovery against fleet load during a sustained outage. ADVISORY_REFRESH_BACKOFF_SECONDS = 5 class TokenCache: """Thread-safe cache wrapping an :class:`AccessTokenProvider` with two-tier proactive refresh and single-flight semantics. Refresh policy on each :meth:`get_token` call: * No cached token → call provider (blocking), cache, return. * Cached with ``expires_at=None`` → return cached forever (never refresh). * More than ``advisory_refresh_seconds`` remaining → return cached. * Between ``mandatory_refresh_seconds`` and ``advisory_refresh_seconds`` remaining (advisory window) → try provider; on success swap cache; on failure log a warning and return the stale cached token. If another caller is already refreshing, the advisory caller just returns the cached token — no second refresh, no waiting. * Less than ``mandatory_refresh_seconds`` remaining or already expired (mandatory window) → call provider; on failure RAISE. Concurrent mandatory callers wait on a shared ``Event`` so exactly one provider call is in flight. The lock is released before the provider call so a 30-second HTTP POST doesn't serialize unrelated callers through a single thread. This matters under async: ``asyncify(get_token)`` runs on the thread pool, and holding the lock across the network call would pin an async worker for the whole exchange. """ def __init__( self, provider: AccessTokenProvider, *, advisory_refresh_seconds: int = ADVISORY_REFRESH_SECONDS, mandatory_refresh_seconds: int = MANDATORY_REFRESH_SECONDS, time_source: Callable[[], float] = time.time, ) -> None: self._provider = provider self._advisory = advisory_refresh_seconds self._mandatory = mandatory_refresh_seconds self._time_source = time_source self._lock = threading.Lock() self._cached: Optional[AccessToken] = None # Set when a refresh is in flight. Waiters in the mandatory window # block on this event; the leader clears it after publishing the # fresh token (or on failure). self._refresh_event: Optional[threading.Event] = None # One-shot: invalidate() sets it; next provider call passes # force_refresh=True so on-disk providers don't re-serve a stale token. self._next_force = False # Time of last advisory-refresh failure (never reset on success — # only distance-from-now matters). self._last_advisory_failure_time: float = 0.0 def _invoke_provider(self, *, force: bool) -> AccessToken: """Invoke ``self._provider``, tolerating legacy zero-arg callables.""" try: return self._provider(force_refresh=force) except TypeError as err: # Back-compat for legacy zero-arg providers. Argument-binding # TypeErrors fire before the body runs, so this can't double-invoke; # a TypeError from inside the provider won't mention the kwarg name. if "force_refresh" not in str(err): raise return self._provider() # type: ignore[call-arg] def _call_provider(self) -> AccessToken: """Call the provider, retrying once on a 401 from the token endpoint.""" # Read but don't clear yet — clearing only on success keeps the flag # alive across a transient failure so the retry still forces. with self._lock: force = self._next_force try: result = self._invoke_provider(force=force) except WorkloadIdentityError as err: if err.status_code != 401: raise log.debug("Token provider returned 401; retrying once") result = self._invoke_provider(force=True) with self._lock: self._next_force = False return result def get_token(self) -> str: """Return a valid bearer token, refreshing if necessary.""" while True: advisory_fallback: Optional[AccessToken] = None remaining_seconds = 0 with self._lock: cached = self._cached if cached is not None: if cached.expires_at is None: return cached.token remaining = cached.expires_at - self._time_source() if remaining > self._advisory: return cached.token if remaining > self._mandatory: # Advisory window. If a refresh is already running, # keep serving the cached token — don't queue and # don't start a second refresh. if self._refresh_event is not None: return cached.token # Backoff: skip refresh and serve cached after a # recent advisory failure. if self._time_source() - self._last_advisory_failure_time < ADVISORY_REFRESH_BACKOFF_SECONDS: return cached.token advisory_fallback = cached remaining_seconds = int(remaining) if self._refresh_event is not None: # Mandatory-window caller with a refresh in flight: wait. waiter_event: Optional[threading.Event] = self._refresh_event else: # We're the leader. self._refresh_event = threading.Event() waiter_event = None if waiter_event is not None: waiter_event.wait() # Loop back and re-read the cache — the refresh may have # succeeded (return fresh token), failed (start a new # refresh ourselves), or been invalidated in between. continue # Leader: run the provider outside the lock. The except catches # BaseException (not a narrow tuple) so the refresh event is # always released — a user-supplied provider raising e.g. # RuntimeError must not deadlock mandatory-window waiters. try: fresh = self._call_provider() except BaseException as err: with self._lock: released = self._refresh_event self._refresh_event = None assert released is not None released.set() if advisory_fallback is not None and isinstance(err, (AnthropicError, httpx.HTTPError)): log.warning( "Advisory token refresh failed (%ds remaining); serving cached token: %s", remaining_seconds, err, ) with self._lock: self._last_advisory_failure_time = self._time_source() return advisory_fallback.token raise with self._lock: self._cached = fresh released = self._refresh_event self._refresh_event = None assert released is not None released.set() return fresh.token def invalidate(self) -> None: """Clear the cached token so the next :meth:`get_token` re-invokes the provider. Also sets a one-shot ``force_refresh`` flag so on-disk providers skip their freshness short-circuit instead of re-serving the revoked token. """ with self._lock: self._cached = None self._next_force = True anthropic-sdk-python-0.120.2/src/anthropic/lib/credentials/_chain.py000066400000000000000000000150131523216435200253600ustar00rootroot00000000000000from __future__ import annotations import os from typing import Optional from ._types import CredentialResult, IdentityTokenProvider from ._workload import WorkloadIdentityCredentials from ._constants import ( ENV_SCOPE, ENV_API_KEY, ENV_PROFILE, ENV_AUTH_TOKEN, ENV_CONFIG_DIR, ENV_WORKSPACE_ID, ENV_IDENTITY_TOKEN, ENV_ORGANIZATION_ID, ENV_FEDERATION_RULE_ID, ENV_SERVICE_ACCOUNT_ID, _has_active_profile_config, _has_explicit_active_config, resolve_identity_token_path, ) from ._providers import StaticToken, CredentialsFile, IdentityTokenFile from ..._exceptions import AnthropicError __all__ = ["default_credentials"] def _build_federation_result(*, base_url: str) -> Optional[CredentialResult]: """Build a :class:`CredentialResult` for the env-var federation path (step 4 in the precedence spec). Returns ``None`` if the required trio isn't fully set.""" federation_rule_id = os.environ.get(ENV_FEDERATION_RULE_ID) organization_id = os.environ.get(ENV_ORGANIZATION_ID) has_literal_token = ENV_IDENTITY_TOKEN in os.environ identity_token_path = resolve_identity_token_path() if not federation_rule_id or not organization_id: return None if not has_literal_token and identity_token_path is None: return None identity_provider: IdentityTokenProvider if identity_token_path is not None: identity_provider = IdentityTokenFile(identity_token_path) else: # Read the env var on every call so a rotated value is picked up # at the next token exchange (don't capture into a closure). def _read_env_token() -> str: value = os.environ.get(ENV_IDENTITY_TOKEN) if value is None: raise AnthropicError( f"{ENV_IDENTITY_TOKEN} is not set; the workload-identity chain " f"selected this provider at construction time but the env var " f"is no longer present." ) return value identity_provider = _read_env_token provider = WorkloadIdentityCredentials( identity_token_provider=identity_provider, federation_rule_id=federation_rule_id, organization_id=organization_id, service_account_id=os.environ.get(ENV_SERVICE_ACCOUNT_ID), # Coerce empty string to None so a defaulted-but-empty CI variable # doesn't put ``"workspace_id": ""`` on the wire — matches the falsy # skip in :func:`._providers._fill_missing_from_env`. workspace_id=os.environ.get(ENV_WORKSPACE_ID) or None, scope=os.environ.get(ENV_SCOPE), ) provider.bind_base_url(base_url) return CredentialResult(provider=provider) def default_credentials(*, base_url: str = "https://api.anthropic.com") -> Optional[CredentialResult]: """Resolve a :class:`CredentialResult` from the environment per the credential-resolution spec. First match wins. Implements steps 2-5 of the spec precedence chain (step 1 is handled at the client constructor level, above this function): Step 2a: ``ANTHROPIC_API_KEY`` → return ``None`` so the client uses its existing ``X-Api-Key`` header path. (API keys are not Bearer tokens, so they can't flow through this chain.) Step 2b: ``ANTHROPIC_AUTH_TOKEN`` → :class:`StaticToken` (Bearer). Step 3: ``ANTHROPIC_PROFILE`` / ``ANTHROPIC_CONFIG_DIR`` set, or the ``active_config`` pointer file exists → load that profile. This is *explicit profile selection*; failures propagate. Step 4: ``ANTHROPIC_FEDERATION_RULE_ID`` + ``ANTHROPIC_ORGANIZATION_ID`` + ``ANTHROPIC_IDENTITY_TOKEN[_FILE]`` → direct jwt-bearer exchange via :class:`WorkloadIdentityCredentials`. Critically, step 4 sits **between** explicit profile (step 3) and fallback profile (step 5): a machine with WIF env vars wired up must use WIF even if a leftover ``default`` profile exists on disk, but a user who explicitly ``ANTHROPIC_PROFILE=dev`` still gets their profile. Step 5: Fallback active profile from disk (``configs/default.json`` or whatever ``active_config`` points at). Errors at this step are swallowed and the chain falls through — a corrupt unselected profile shouldn't break an otherwise-explicit api_key= path. Returns ``None`` when nothing matches — the client will fall back to its normal "no auth configured" error. """ # Step 2a — env api_key: return None so the base client handles X-Api-Key. if os.environ.get(ENV_API_KEY): return None # Step 2b — env auth_token: static bearer. auth_token = os.environ.get(ENV_AUTH_TOKEN) if auth_token: return CredentialResult(provider=StaticToken(auth_token)) # Step 3 — explicit profile selection (ANTHROPIC_PROFILE / ANTHROPIC_CONFIG_DIR # / active_config pointer). Failures propagate — a user who explicitly # names a profile expects a broken config to surface, not to fall through. env_explicit = bool(os.environ.get(ENV_PROFILE) or os.environ.get(ENV_CONFIG_DIR)) pointer_explicit = _has_explicit_active_config() if env_explicit or pointer_explicit: creds_file = CredentialsFile() creds_file.bind_base_url(base_url) extra_headers = creds_file.extra_headers() return CredentialResult( provider=creds_file, extra_headers=extra_headers, base_url=creds_file.resolved_base_url, ) # Step 4 — env-var workload identity federation. Sits above the # fallback on-disk profile so a machine with WIF env vars uses WIF # even if a leftover ``default`` profile exists on disk. federation_result = _build_federation_result(base_url=base_url) if federation_result is not None: return federation_result # Step 5 — fallback active profile from disk. Errors are swallowed and # the chain falls through because the user didn't explicitly select # this profile; a corrupt auto-discovered config shouldn't break # construction. if _has_active_profile_config(): creds_file = CredentialsFile() creds_file.bind_base_url(base_url) try: extra_headers = creds_file.extra_headers() except AnthropicError: return None return CredentialResult( provider=creds_file, extra_headers=extra_headers, base_url=creds_file.resolved_base_url, ) return None anthropic-sdk-python-0.120.2/src/anthropic/lib/credentials/_constants.py000066400000000000000000000243011523216435200263120ustar00rootroot00000000000000from __future__ import annotations import os import sys import pathlib from typing import Optional from ..._exceptions import AnthropicError GRANT_TYPE_JWT_BEARER = "urn:ietf:params:oauth:grant-type:jwt-bearer" GRANT_TYPE_REFRESH_TOKEN = "refresh_token" TOKEN_ENDPOINT = "/v1/oauth/token" # Seconds to wait on the /v1/oauth/token POST before giving up. Tokens are cheap # to mint and the handler is fast; a long timeout mostly means a sick backend. TOKEN_EXCHANGE_TIMEOUT = 30.0 # Beta header required on any authenticated API request that uses a Bearer # token obtained via OAuth/federation (unlocks `Authorization: Bearer` auth # at all), and on refresh_token grants against /v1/oauth/token. OAUTH_API_BETA_HEADER = "oauth-2025-04-20" # Beta header routing switch for /v1/oauth/token jwt-bearer grants. Presence # routes the POST to the api-go userauth handler (jwt-bearer only); absence # routes it to the Python oauth_server (authorization_code / refresh_token). # MUST only be sent on jwt-bearer exchanges — sending it on refresh_token would # misroute the request to userauth and fail with "unsupported grant_type". FEDERATION_BETA_HEADER = "oidc-federation-2026-04-01" # Proactive refresh thresholds (seconds before expiry). Tuned for ≤10min token TTL. ADVISORY_REFRESH_SECONDS = 120 MANDATORY_REFRESH_SECONDS = 30 DEFAULT_PROFILE = "default" DEFAULT_BASE_URL = "https://api.anthropic.com" # Env vars — explicit auth (tier 0) ENV_API_KEY = "ANTHROPIC_API_KEY" ENV_AUTH_TOKEN = "ANTHROPIC_AUTH_TOKEN" # Env vars — config dir + profile selection (tier 1) ENV_CONFIG_DIR = "ANTHROPIC_CONFIG_DIR" ENV_PROFILE = "ANTHROPIC_PROFILE" # Env vars — direct workload identity, bypassing config files (tier 2) ENV_IDENTITY_TOKEN = "ANTHROPIC_IDENTITY_TOKEN" ENV_IDENTITY_TOKEN_FILE = "ANTHROPIC_IDENTITY_TOKEN_FILE" ENV_FEDERATION_RULE_ID = "ANTHROPIC_FEDERATION_RULE_ID" ENV_ORGANIZATION_ID = "ANTHROPIC_ORGANIZATION_ID" ENV_SERVICE_ACCOUNT_ID = "ANTHROPIC_SERVICE_ACCOUNT_ID" ENV_WORKSPACE_ID = "ANTHROPIC_WORKSPACE_ID" ENV_SCOPE = "ANTHROPIC_SCOPE" ENV_BASE_URL = "ANTHROPIC_BASE_URL" def _user_agent() -> str: # pyright: ignore[reportUnusedFunction] — used by _workload/_providers """``User-Agent`` value sent on token-endpoint POSTs. Computed lazily so this module doesn't need to import ``_version`` at module load time (the credentials package is otherwise import-light). """ from ..._version import __version__ return f"anthropic-python/{__version__}" def _config_dir() -> pathlib.Path: """Resolve the config directory. ``ANTHROPIC_CONFIG_DIR`` env var → platform default. Platform defaults: * Linux & macOS: ``~/.config/anthropic/`` — XDG-style on both platforms for consistency across SDKs (macOS does **not** use ``~/Library/Application Support/``). * Windows: ``%APPDATA%\\Anthropic\\`` """ env = os.environ.get(ENV_CONFIG_DIR) if env: return pathlib.Path(env) if sys.platform == "win32": appdata = os.environ.get("APPDATA") base = pathlib.Path(appdata) if appdata else pathlib.Path.home() / "AppData" / "Roaming" return base / "Anthropic" return pathlib.Path.home() / ".config" / "anthropic" def _read_active_config_pointer() -> Optional[str]: """Return the stripped contents of ``/active_config``, or ``None`` if the pointer file is missing or empty.""" try: name = (_config_dir() / "active_config").read_text(encoding="utf-8").strip() except OSError: return None return name or None def _active_profile() -> str: # pyright: ignore[reportUnusedFunction] — used by _providers """Resolve the active profile name. ``ANTHROPIC_PROFILE`` env var → ``/active_config`` pointer file → ``"default"`` literal. The resolved name is validated against path- traversal patterns before being returned. """ env = os.environ.get(ENV_PROFILE) if env: _validate_profile_name(env, source=ENV_PROFILE) return env name = _read_active_config_pointer() if name is None: return DEFAULT_PROFILE _validate_profile_name(name, source="active_config pointer file") return name def _require_https(url: str, *, field: str) -> None: # pyright: ignore[reportUnusedFunction] — used by _workload/_providers """Reject non-``https://`` token-endpoint URLs. Localhost is allowed for testing so ``base_url="http://localhost:8080"`` works against a local ``oauth_server`` instance; everything else must be TLS-encrypted because the body of these POSTs carries the assertion JWT or a long-lived refresh token. """ lowered = url.lower().rstrip("/") if lowered.startswith("https://"): return if lowered.startswith(("http://localhost", "http://127.0.0.1", "http://[::1]")): return raise AnthropicError( f"{field} must use https (got {url!r}); the token-exchange endpoint " f"carries secret material and cannot be used over cleartext HTTP." ) def _validate_profile_name(profile: str, *, source: str = "profile name") -> None: """Reject profile names that could escape the config directory. Profile names come from user-controlled sources (``ANTHROPIC_PROFILE``, the ``active_config`` pointer file, ``CredentialsFile(profile=...)``) and are interpolated into filesystem paths. A value like ``"../../etc/shadow"`` would otherwise let a read of ``configs/.json`` escape the config root entirely. Pass ``source=`` so the error message names where the bad value came from. """ if not profile: raise AnthropicError(f"{source} must not be empty.") if profile != profile.strip(): raise AnthropicError(f"{source} {profile!r} has leading or trailing whitespace.") if profile.startswith("."): raise AnthropicError(f"{source} {profile!r} must not start with a dot.") for sep in ("/", "\\", os.sep): if sep and sep in profile: raise AnthropicError( f"{source} {profile!r} must not contain path separators — " f"profiles are filenames under the config directory. Pick a name without {sep!r}." ) if "\x00" in profile: raise AnthropicError(f"{source} {profile!r} must not contain null bytes.") def _resolve_under(base: pathlib.Path, candidate: pathlib.Path) -> pathlib.Path: """Assert ``candidate`` resolves to a descendant of ``base``, return it verbatim. The containment check uses ``resolve(strict=False)`` on both sides so symlinks and ``..`` segments are normalized for the purposes of escape detection. The returned path is the *original* (unresolved) candidate — callers that care about symlink following must handle it themselves (e.g. ``os.stat(follow_symlinks=False)``). """ base_resolved = base.resolve(strict=False) candidate_resolved = candidate.resolve(strict=False) try: candidate_resolved.relative_to(base_resolved) except ValueError as err: raise AnthropicError(f"Resolved path {candidate_resolved} escapes config directory {base_resolved}.") from err return candidate def _config_file_path(profile: str) -> pathlib.Path: # pyright: ignore[reportUnusedFunction] — used by _providers """Path to ``/configs/.json`` (non-secret, 0644).""" _validate_profile_name(profile) base = _config_dir() return _resolve_under(base, base / "configs" / f"{profile}.json") def _credentials_file_path(profile: str) -> pathlib.Path: # pyright: ignore[reportUnusedFunction] — used by _providers """Path to ``/credentials/.json`` (secret, 0600).""" _validate_profile_name(profile) base = _config_dir() return _resolve_under(base, base / "credentials" / f"{profile}.json") def _has_active_profile_config() -> bool: # pyright: ignore[reportUnusedFunction] — used by _chain """Tighter auto-discover check for the tier-1 credential chain. Returns ``True`` only if the *active* profile's config file exists. The previous version returned ``True`` for any ``.json`` under ``configs/``, which meant a stray ``configs/work.json`` on disk was enough to steer ``default_credentials()`` into reading ``configs/default.json`` and failing because ``default.json`` wasn't there. """ try: return _config_file_path(_active_profile()).is_file() except (OSError, AnthropicError): return False def _has_explicit_active_config() -> bool: # pyright: ignore[reportUnusedFunction] — used by _chain """True if the user wrote a non-empty ``active_config`` pointer file. This is an explicit opt-in signal equivalent to setting ``ANTHROPIC_PROFILE``: the user has told us which profile to load. If the target config file is missing or malformed, the chain should surface that error rather than silently falling through — matching how ``ANTHROPIC_PROFILE=missing`` behaves today. """ return _read_active_config_pointer() is not None def resolve_identity_token_path(path: str | os.PathLike[str] | None = None) -> pathlib.Path | None: """ctor arg → ``ANTHROPIC_IDENTITY_TOKEN_FILE`` → ``None``.""" if path is not None: return pathlib.Path(path) env = os.environ.get(ENV_IDENTITY_TOKEN_FILE) if env: return pathlib.Path(env) return None def _has_auto_discoverable_credentials() -> bool: # pyright: ignore[reportUnusedFunction] — used by _client """True if the environment / filesystem contains signals that would normally drive the tier-1 (profile) or tier-2 (env federation) paths of :func:`default_credentials`. Used by the shadow-warning detection in the client constructor: if a static ``ANTHROPIC_API_KEY`` / ``ANTHROPIC_AUTH_TOKEN`` is set alongside any of these signals, the auto-discovery would have yielded a credential but got silently shadowed — and the user should know. """ if os.environ.get(ENV_PROFILE) or os.environ.get(ENV_CONFIG_DIR): return True if _has_explicit_active_config(): return True if os.environ.get(ENV_FEDERATION_RULE_ID) and os.environ.get(ENV_ORGANIZATION_ID): if os.environ.get(ENV_IDENTITY_TOKEN_FILE) or os.environ.get(ENV_IDENTITY_TOKEN): return True return False anthropic-sdk-python-0.120.2/src/anthropic/lib/credentials/_providers.py000066400000000000000000001162201523216435200263150ustar00rootroot00000000000000from __future__ import annotations import os import json import stat import time import logging import pathlib import tempfile from typing import TYPE_CHECKING, Any, Dict, Union, Optional, cast from typing_extensions import override import httpx from ._types import AccessToken, IdentityTokenProvider from ._secrets import ( SecretStr, _unwrap_secret, _strip_traceback, _json_dumps_secrets, _wrap_secret_fields, _NonObjectPayloadError, ) from ._constants import ( ENV_SCOPE, ENV_PROFILE, ENV_BASE_URL, ENV_AUTH_TOKEN, ENV_CONFIG_DIR, TOKEN_ENDPOINT, DEFAULT_BASE_URL, ENV_WORKSPACE_ID, ENV_ORGANIZATION_ID, OAUTH_API_BETA_HEADER, ENV_FEDERATION_RULE_ID, ENV_SERVICE_ACCOUNT_ID, TOKEN_EXCHANGE_TIMEOUT, ENV_IDENTITY_TOKEN_FILE, GRANT_TYPE_REFRESH_TOKEN, MANDATORY_REFRESH_SECONDS, _user_agent, _require_https, _active_profile, _config_file_path, _credentials_file_path, resolve_identity_token_path, ) from ..._exceptions import AnthropicError log: logging.Logger = logging.getLogger(__name__) if TYPE_CHECKING: from ._workload import WorkloadIdentityCredentials __all__ = ["StaticToken", "EnvToken", "CredentialsFile", "InMemoryConfig", "IdentityTokenFile"] def _coerce_expires_at(value: Any, source: Optional[pathlib.Path]) -> Optional[int]: """Parse a credentials-file ``expires_at`` field into Unix seconds.""" if value is None: return None try: return int(value) except (TypeError, ValueError) as err: where = f"credentials file at {source}" if source is not None else "credentials" raise AnthropicError( f"{where} has invalid 'expires_at' {value!r}; expected an integer " f"Unix timestamp in seconds. The SDK does not parse ISO8601 — convert " f"with int(datetime.timestamp()) before writing the file." ) from err # Discriminator written to credentials/.json. Only one value in v1; # future credential shapes (e.g. private key material) get their own. CREDENTIALS_FILE_TYPE = "oauth_token" # On-disk file-format versions. Absent on read = 1 (current shape). CONFIG_FILE_VERSION = "1.0" CREDENTIALS_FILE_VERSION = "1.0" # Discriminator values for the config file's ``authentication.type`` field. AUTH_TYPE_OIDC_FEDERATION = "oidc_federation" AUTH_TYPE_USER_OAUTH = "user_oauth" def _fill_missing_from_env(config: Dict[str, Any], auth: Dict[str, Any]) -> None: """Fill empty profile fields from corresponding ANTHROPIC_* env vars. The profile file is authoritative — this only fills fields the file left unset. Empty-string env values are treated as unset. """ def fill(target: Dict[str, Any], key: str, env_var: str) -> None: # Absent-key and empty-string profile values are both treated as unset. if not target.get(key): v = os.environ.get(env_var) if v: target[key] = v fill(config, "base_url", ENV_BASE_URL) fill(config, "organization_id", ENV_ORGANIZATION_ID) fill(config, "workspace_id", ENV_WORKSPACE_ID) auth_type = auth.get("type") if auth_type == AUTH_TYPE_OIDC_FEDERATION: fill(auth, "federation_rule_id", ENV_FEDERATION_RULE_ID) fill(auth, "service_account_id", ENV_SERVICE_ACCOUNT_ID) fill(auth, "scope", ENV_SCOPE) if not auth.get("identity_token"): v = os.environ.get(ENV_IDENTITY_TOKEN_FILE) if v: auth["identity_token"] = {"source": "file", "path": v} elif auth_type == AUTH_TYPE_USER_OAUTH: fill(auth, "scope", ENV_SCOPE) class StaticToken: """An :class:`AccessTokenProvider` that always returns a fixed token with no expiry.""" def __init__(self, token: str) -> None: self._token = token def __call__(self, *, force_refresh: bool = False) -> AccessToken: del force_refresh # no provider-side cache to bypass return AccessToken(token=self._token, expires_at=None) class EnvToken: """An :class:`AccessTokenProvider` that reads ``ANTHROPIC_AUTH_TOKEN`` at call time.""" def __init__(self, env_var: str = ENV_AUTH_TOKEN) -> None: self._env_var = env_var def __call__(self, *, force_refresh: bool = False) -> AccessToken: del force_refresh value = os.environ.get(self._env_var) if value is None: raise AnthropicError( f"Environment variable {self._env_var} is not set. " f"Set it or pass an explicit `credentials=` provider to the client." ) return AccessToken(token=value, expires_at=None) class CredentialsFile: """An :class:`AccessTokenProvider` backed by a named profile. A profile is a pair of files under the config directory (``~/.config/anthropic/`` by default; override with ``ANTHROPIC_CONFIG_DIR``): * ``configs/.json`` — non-secret. Holds the nested ``"authentication"`` object (discriminated by its ``"type"`` field), plus top-level ``organization_id``, ``workspace_id``, and ``base_url``. The ``authentication`` object may contain a ``credentials_path`` field overriding the credentials file location. * ``credentials/.json`` — secret (0600). Holds ``access_token``, ``expires_at``, and (for ``user_oauth`` with a ``client_id``) ``refresh_token``. The split keeps secret material out of files that may need to be readable by config-only consumers, and lets the SDK enforce 0600 on the credentials file without locking out config readers. Dispatches on the ``authentication.type`` discriminator: ``"oidc_federation"`` OIDC workload identity federation. Lazily constructs a :class:`WorkloadIdentityCredentials` delegate from the nested auth fields plus the top-level ``organization_id`` and calls it to perform the jwt-bearer exchange. ``"user_oauth"`` Output of an interactive PKCE login. If the auth block has a ``client_id``, performs ``refresh_token`` grants on expiry and writes the new tokens back to the credentials file (atomic replace, refresh-token rotation supported). Without a ``client_id``, the credentials file is treated as externally rotated — the SDK re-reads it on every invocation and returns whatever ``access_token`` is there, no refresh grant attempted. This is the pattern for a sidecar/daemon that mints the access token out-of-band. Args: profile: Profile name. ``None`` resolves via ``ANTHROPIC_PROFILE`` env → ``/active_config`` pointer file → ``"default"``. """ def __init__( self, profile: Optional[str] = None, *, http_client: Optional[httpx.Client] = None, ) -> None: self._profile = profile if profile is not None else _active_profile() self._config_path = _config_file_path(self._profile) self._bound_base_url: Optional[str] = None self._http_client = http_client self._owned_http_client: Optional[httpx.Client] = None # Populated on first __call__ — keeps construction cheap and exception-free # so the chain can construct us optimistically after an existence check. self._config: Optional[Dict[str, Any]] = None self._credentials_path: Optional[pathlib.Path] = None self._base_url: str = DEFAULT_BASE_URL self._workload_delegate: Optional[WorkloadIdentityCredentials] = None @property def profile(self) -> str: return self._profile @property def config_path(self) -> pathlib.Path: return self._config_path @property def resolved_base_url(self) -> Optional[str]: """The ``base_url`` declared in the profile config file, if any. Returns ``None`` when the config has no top-level ``base_url`` key — callers should fall back to their own default rather than the provider's bound/default value, so a profile that *doesn't* pin a host never overrides an explicit client setting. Loads the config on first access. """ config = self._load_config() raw = config.get("base_url") return str(raw).rstrip("/") if raw else None def bind_base_url(self, base_url: str) -> None: """Adopt the owning client's ``base_url`` as a fallback for the token exchange. Slots between the config file's own ``base_url`` field and the hard-coded default; a ``base_url`` in the config file still wins. The owning client binds exactly once at construction; sharing one instance across clients with different ``base_url`` values is unsupported and silently picks the last bind when the config file doesn't pin a host. """ bound = base_url.rstrip("/") # Validate eagerly so an invalid bind fails at bind time, not at the # subsequent _load_config() — matches WorkloadIdentityCredentials. _require_https(bound, field=f"{self._config_path}: base_url") self._bound_base_url = bound if self._config is not None: self._base_url = self._resolve_base_url(self._config) _require_https(self._base_url, field=f"{self._config_path}: base_url") def _resolve_base_url(self, config: Dict[str, Any]) -> str: """base_url precedence: top-level config field → bound (the owning client's base_url, via :meth:`bind_base_url`) → default. Validated against the scheme/TLS rules so a malicious config with ``base_url="http://evil/"`` can't exfiltrate the assertion or refresh token.""" if config.get("base_url"): return str(config["base_url"]).rstrip("/") if self._bound_base_url is not None: return self._bound_base_url return DEFAULT_BASE_URL def extra_headers(self) -> Dict[str, str]: """Return headers derived from the config file (e.g. ``workspace_id``). Eagerly reads the config if not yet loaded. The returned dict is suitable for merging into the client's default headers. """ config = self._load_config() headers: Dict[str, str] = {} # For federation profiles workspace_id is sent in the jwt-bearer # exchange body, not as a request header (the minted token is already # workspace-scoped, so the header would be ignored). if self._auth_block().get("type") != AUTH_TYPE_OIDC_FEDERATION: workspace_id = config.get("workspace_id") if workspace_id: headers["anthropic-workspace-id"] = str(workspace_id) return headers # -- file IO ----------------------------------------------------------- def _load_config(self) -> Dict[str, Any]: """Read and cache the config file, resolving ``base_url`` and ``credentials_path``.""" if self._config is not None: return self._config try: raw = self._config_path.read_text(encoding="utf-8") except FileNotFoundError as err: raise AnthropicError( f"Config file not found at {self._config_path} (profile {self._profile!r}). " f"Set {ENV_PROFILE} to select a different profile, or set {ENV_CONFIG_DIR} " f"to relocate the config directory." ) from err except (OSError, UnicodeDecodeError) as err: raise AnthropicError(f"Config file at {self._config_path} could not be read: {err}") from err try: raw_config: Any = json.loads(raw) except json.JSONDecodeError as err: raise AnthropicError(f"Config file at {self._config_path} is not valid JSON: {err}") from err if not isinstance(raw_config, dict): raise AnthropicError( f"Config file at {self._config_path} must contain a JSON object, not {type(raw_config).__name__}." ) config = cast("Dict[str, Any]", raw_config) raw_auth = config.get("authentication") if not isinstance(raw_auth, dict): raise AnthropicError( f"Config file at {self._config_path} is missing the 'authentication' object. " f'Expected shape: {{"authentication": {{"type": ' f'"{AUTH_TYPE_OIDC_FEDERATION}"|"{AUTH_TYPE_USER_OAUTH}", ...}}, ...}}' ) auth = cast("Dict[str, Any]", raw_auth) # Env-vars fill only what the file left empty; runs before derived # state (base_url, identity_token path) is resolved. _fill_missing_from_env(config, auth) self._base_url = self._resolve_base_url(config) _require_https(self._base_url, field=f"{self._config_path}: base_url") override = auth.get("credentials_path") if override: self._credentials_path = pathlib.Path(str(override)).expanduser() else: self._credentials_path = _credentials_file_path(self._profile) self._config = config return config def _read_credentials(self) -> Dict[str, Any]: """Read the credentials file. Re-reads on every call — daemons rotate it. Secret values in the returned dict (every string field not in ``_secrets._PLAIN_KEYS``) are :class:`SecretStr`-wrapped — unwrap with ``_unwrap_secret`` at the point of use. Writing the dict back through :meth:`_atomic_write_credentials` unwraps automatically. On Unix, verifies the file is not group/world-readable. World-readable credentials files are refused outright; group-readable files log a warning but are accepted. The check is skipped on Windows where POSIX mode bits don't carry the same meaning. """ assert self._credentials_path is not None # set by _load_config path = self._credentials_path if os.name == "posix": try: file_stat = os.stat(path, follow_symlinks=False) except FileNotFoundError as err: raise AnthropicError(f"Credentials file not found at {path} (profile {self._profile!r}).") from err except OSError as err: raise AnthropicError(f"Credentials file at {path} could not be accessed: {err}") from err if stat.S_ISLNK(file_stat.st_mode): raise AnthropicError( f"Credentials file at {path} is a symlink; refusing to follow " f"(move the real file into place to keep secret material on the expected filesystem)." ) mode = stat.S_IMODE(file_stat.st_mode) if mode & 0o004: raise AnthropicError( f"Credentials file at {path} is world-readable (mode {mode:#o}); " f"run `chmod 600 {path}` before retrying." ) if mode & 0o070: log.warning( "Credentials file at %s is group-readable (mode %#o); consider `chmod 600 %s`.", path, mode, path, ) try: # Read → parse → wrap in one expression: neither the raw file text # nor an unwrapped token dict is ever bound to a local in this # frame, so traceback frame locals stay free of credential # material on every error path in and below this method. creds: Dict[str, Any] = _wrap_secret_fields(json.loads(path.read_text(encoding="utf-8"))) except FileNotFoundError as err: raise AnthropicError(f"Credentials file not found at {path} (profile {self._profile!r}).") from err except json.JSONDecodeError as err: # The JSONDecodeError message carries only position info. raise AnthropicError(f"Credentials file at {path} is not valid JSON: {err}") from _strip_traceback(err) except _NonObjectPayloadError as err: # Rejected inside the helper with the payload unbound — a scalar # credentials file is still secret material (e.g. a bare token). raise AnthropicError( f"Credentials file at {path} must contain a JSON object, not {err.type_name}." ) from None except (OSError, UnicodeDecodeError) as err: raise AnthropicError(f"Credentials file at {path} could not be read: {err}") from err # Validate discriminator if present; lenient if absent so hand-written # or older files keep working. Catches config/credentials drift early. actual = creds.get("type") if actual is not None and actual != CREDENTIALS_FILE_TYPE: assert self._config is not None # _load_config always precedes _read_credentials auth_type = self._config["authentication"].get("type") raise AnthropicError( f"credentials file has type {actual!r}; expected {CREDENTIALS_FILE_TYPE!r} " f"for authentication.type {auth_type!r}" ) return creds def _get_http_client(self) -> httpx.Client: """Return an ``httpx.Client``, lazily creating (and tracking) one we own.""" if self._http_client is not None: return self._http_client if self._owned_http_client is None: self._owned_http_client = httpx.Client(timeout=TOKEN_EXCHANGE_TIMEOUT) return self._owned_http_client def close(self) -> None: """Close the owned ``httpx.Client`` if we created one.""" if self._owned_http_client is not None: self._owned_http_client.close() self._owned_http_client = None if self._workload_delegate is not None: self._workload_delegate.close() def reload(self) -> None: """Drop the cached config so the next call re-reads it from disk. ``CredentialsFile`` caches the parsed config across calls to keep the hot path cheap; a daemon that rotates a profile in place (e.g. flips ``"type": "user_oauth"`` to ``"type": "oidc_federation"``) will not be picked up automatically. Callers that need to react to such changes can call ``reload()`` to force a fresh read on the next ``__call__``. """ self._config = None self._workload_delegate = None def _atomic_write_credentials(self, data: Dict[str, Any]) -> None: """Atomic write to the credentials file (NOT the config file). ``data`` may hold :class:`SecretStr` token values (see :meth:`_read_credentials`); they are unwrapped at dump time, so the on-disk format is unchanged and this frame's locals stay redacted if the write fails (e.g. ENOSPC) with a crash reporter capturing them. """ assert self._credentials_path is not None parent = self._credentials_path.parent parent.mkdir(parents=True, exist_ok=True, mode=0o700) # mkstemp gives a unique temp name so concurrent writers (e.g. # gunicorn workers cold-starting together) don't race on a fixed # ``.tmp`` path; whichever os.replace lands last wins, which is fine # for a best-effort cache. fd, tmp = tempfile.mkstemp(dir=parent, prefix=f".{self._credentials_path.name}.", suffix=".tmp") try: try: os.fchmod(fd, 0o600) os.write(fd, _json_dumps_secrets(data, indent=2)) os.fsync(fd) finally: os.close(fd) os.replace(tmp, self._credentials_path) except BaseException: try: os.unlink(tmp) except OSError: pass raise # fsync the parent directory so the rename itself survives a crash on # filesystems that defer directory-entry writes. Best-effort: Windows # and some POSIX flavours don't support directory fds. try: dir_fd = os.open(parent, os.O_RDONLY) try: os.fsync(dir_fd) finally: os.close(dir_fd) except OSError: pass # -- dispatch ---------------------------------------------------------- def _auth_block(self) -> Dict[str, Any]: """Return the cached ``authentication`` sub-object from the config file.""" config = self._load_config() return cast("Dict[str, Any]", config["authentication"]) def __call__(self, *, force_refresh: bool = False) -> AccessToken: auth = self._auth_block() auth_type = auth.get("type") if auth_type == AUTH_TYPE_OIDC_FEDERATION: return self._call_oidc_federation(auth, force_refresh=force_refresh) if auth_type == AUTH_TYPE_USER_OAUTH: return self._call_user_oauth(auth, force_refresh=force_refresh) raise AnthropicError( f"Unknown authentication.type {auth_type!r} at {self._config_path}. " f"Expected {AUTH_TYPE_OIDC_FEDERATION!r} or {AUTH_TYPE_USER_OAUTH!r}." ) # -- "user_oauth" ----------------------------------------------------- def _call_user_oauth(self, auth: Dict[str, Any], *, force_refresh: bool = False) -> AccessToken: """Interactive-login profile. With a ``client_id`` in the auth block, we run the refresh_token grant on expiry; without one, we treat the credentials file as externally rotated and just read it fresh. """ from ._workload import WorkloadIdentityError, _request_id, _raise_token_endpoint_error creds = self._read_credentials() access_token = creds.get("access_token") if not access_token: raise AnthropicError(f"Credentials file at {self._credentials_path} is missing 'access_token'.") client_id = auth.get("client_id") if not client_id: # No client_id → externally rotated. Return whatever the file has; # a sidecar/daemon is responsible for keeping it fresh. expires_at = _coerce_expires_at(creds.get("expires_at"), self._credentials_path) return AccessToken(token=_unwrap_secret(access_token), expires_at=expires_at) refresh_token = creds.get("refresh_token") if not refresh_token: raise WorkloadIdentityError( f"credentials file for profile {self._profile!r} (authentication.type " f"{AUTH_TYPE_USER_OAUTH!r} with client_id) must include 'refresh_token': " f"{self._credentials_path}" ) # Strict expiry only — TokenCache owns the advisory/mandatory refresh # policy. A second threshold here could trigger a refresh grant while # the outer cache is still serving fine. # force_refresh (set by TokenCache.invalidate after a 401) bypasses # the disk-freshness short-circuit so a revoked token isn't re-served. expires_at = _coerce_expires_at(creds.get("expires_at"), self._credentials_path) if not force_refresh and expires_at is not None and time.time() < expires_at: return AccessToken(token=_unwrap_secret(access_token), expires_at=expires_at) body: Dict[str, Union[str, SecretStr]] = { "grant_type": GRANT_TYPE_REFRESH_TOKEN, "refresh_token": refresh_token, "client_id": client_id, } try: resp = self._get_http_client().post( f"{self._base_url}{TOKEN_ENDPOINT}", # Serialized inline so the raw request bytes are never bound # to a local here; SecretStr values unwrap at dump time. content=_json_dumps_secrets(body), headers={ "Content-Type": "application/json", # oauth-2025-04-20 unlocks the token endpoint family. Do # NOT send oidc-federation-2026-04-01 — that's a routing # switch that misroutes refresh_token grants to the Go # userauth handler, which only accepts jwt-bearer. "anthropic-beta": OAUTH_API_BETA_HEADER, "User-Agent": _user_agent(), }, ) except httpx.HTTPError as err: raise WorkloadIdentityError( f"user_oauth refresh failed to reach token endpoint: {err}" ) from _strip_traceback(err) if resp.status_code != 200: _raise_token_endpoint_error(resp, message_prefix="user_oauth refresh failed") try: payload: Dict[str, Any] = _wrap_secret_fields(resp.json()) except ValueError as err: # A raw JSONDecodeError must not escape as the raised error — its # message names the decoder, not this grant. Matches the # jwt-bearer path's non-JSON handling. raise WorkloadIdentityError( f"user_oauth refresh returned a non-JSON response (status {resp.status_code}).", status_code=resp.status_code, request_id=_request_id(resp), ) from _strip_traceback(err) except _NonObjectPayloadError as err: # Rejected inside the helper with the payload unbound — a # non-object body can echo the request's refresh token. raise WorkloadIdentityError( f"user_oauth refresh returned a JSON {err.type_name} (status {resp.status_code}); expected an object.", status_code=resp.status_code, request_id=_request_id(resp), ) from None new_access = payload.get("access_token") if not new_access: raise WorkloadIdentityError("user_oauth refresh response missing 'access_token'") raw_expires_in = payload.get("expires_in", 3600) try: expires_in = int(raw_expires_in) except (TypeError, ValueError) as err: raise WorkloadIdentityError( f"user_oauth refresh response has invalid 'expires_in' {raw_expires_in!r}; " f"expected an integer number of seconds." ) from err new_expires_at = int(time.time()) + expires_in new_refresh = payload.get("refresh_token") or refresh_token creds["version"] = CREDENTIALS_FILE_VERSION creds["type"] = CREDENTIALS_FILE_TYPE creds["access_token"] = new_access creds["expires_at"] = new_expires_at creds["refresh_token"] = new_refresh # A failed persist propagates: the refresh token may have rotated # server-side, so silently continuing would lose it. self._atomic_write_credentials(creds) return AccessToken(token=_unwrap_secret(new_access), expires_at=new_expires_at) # -- "oidc_federation" ------------------------------------------------ def _read_credentials_if_exists(self) -> Optional[Dict[str, Any]]: """``_read_credentials`` variant that returns ``None`` on absence instead of raising — used by the federation disk-cache path where a missing credentials file just means "exchange now". """ assert self._credentials_path is not None if not self._credentials_path.exists(): return None try: return self._read_credentials() except AnthropicError as err: if isinstance(err.__cause__, FileNotFoundError): return None raise def _call_oidc_federation(self, auth: Dict[str, Any], *, force_refresh: bool = False) -> AccessToken: if self._workload_delegate is None: self._workload_delegate = self._build_workload_delegate(auth) # Disk cache: if a prior exchange wrote credentials/.json and # the token there is unexpired, return it instead of re-exchanging. # The in-memory TokenCache layer applies the proactive 120s/30s policy # on top of this; the disk cache only matters across process restarts. # ``_credentials_path`` is always set for ``CredentialsFile`` proper # (``_load_config`` defaults it); subclasses (``InMemoryConfig``) # leave it ``None`` to opt out of the disk cache entirely. if self._credentials_path is None: return self._workload_delegate() # force_refresh (set by TokenCache.invalidate after a 401) bypasses # the disk-cache short-circuit so a revoked token isn't re-served. cached = self._read_credentials_if_exists() if not force_refresh and cached is not None: access_token = cached.get("access_token") expires_at = cached.get("expires_at") try: if ( access_token and expires_at is not None and time.time() < float(expires_at) - MANDATORY_REFRESH_SECONDS ): return AccessToken(token=str(_unwrap_secret(access_token)), expires_at=int(expires_at)) except (TypeError, ValueError): # corrupted expires_at — fall through to re-exchange and overwrite pass token = self._workload_delegate() try: self._atomic_write_credentials( { **(cached or {}), "version": CREDENTIALS_FILE_VERSION, "type": CREDENTIALS_FILE_TYPE, # Wrapped so a failing write never holds the raw token in # frame locals; unwrapped again at dump time. "access_token": SecretStr(token.token), "expires_at": token.expires_at, } ) except OSError as err: log.debug("federation token disk-cache write-back failed (best-effort): %s", err) return token def _build_workload_delegate(self, auth: Dict[str, Any]) -> WorkloadIdentityCredentials: # Import here to avoid a circular import (_workload imports nothing from # _providers so the dependency is one-way at runtime). from ._workload import WorkloadIdentityError, WorkloadIdentityCredentials federation_rule_id = auth.get("federation_rule_id") assert self._config is not None # _load_config precedes dispatch organization_id = self._config.get("organization_id") if not federation_rule_id or not organization_id: raise WorkloadIdentityError( f"config file with authentication.type {AUTH_TYPE_OIDC_FEDERATION!r} must include " f"'authentication.federation_rule_id' and top-level 'organization_id': " f"{self._config_path}" ) # identity_token is a discriminated object so future variants (url, # executable, aws_sigv4) slot in without renaming. v1 implements # source:"file" only. Absent → fall back to ANTHROPIC_IDENTITY_TOKEN_FILE. identity_token_cfg = auth.get("identity_token") if identity_token_cfg is not None: source = identity_token_cfg.get("source") if source != "file": raise AnthropicError(f"identity_token source {source!r} is not supported; only 'file' is implemented") identity_token_path = identity_token_cfg.get("path") if not identity_token_path: # Empty/missing path is a config bug, not an env-var fallback # signal — the source explicitly says "file", which has no # meaning without a path. Without this check we'd silently # fall through to ANTHROPIC_IDENTITY_TOKEN_FILE and override # user intent. raise AnthropicError( f"identity_token source 'file' requires a non-empty path; " f"profile {self._profile!r} at {self._config_path} has identity_token={identity_token_cfg!r}." ) else: identity_token_path = None provider = IdentityTokenFile(identity_token_path) if identity_token_path else IdentityTokenFile() # The delegate borrows our owned httpx.Client: passing http_client= # sets _owns_http_client=False on the delegate so its close() is a # no-op. CredentialsFile.close() remains the single closer. delegate = WorkloadIdentityCredentials( identity_token_provider=provider, federation_rule_id=federation_rule_id, organization_id=organization_id, service_account_id=auth.get("service_account_id"), workspace_id=self._config.get("workspace_id"), scope=auth.get("scope"), http_client=self._get_http_client(), ) delegate.bind_base_url(self._base_url) return delegate class IdentityTokenFile: """An :class:`IdentityTokenProvider` that reads a JWT from a file on every call. Kubernetes projected service-account tokens (and similar) are rotated in place, so the file MUST be re-read on every invocation rather than cached. """ def __init__(self, path: Union[str, "os.PathLike[str]", None] = None) -> None: resolved = resolve_identity_token_path(path) if resolved is None: raise AnthropicError( f"No identity token file path given. Pass `path=` or set the {ENV_IDENTITY_TOKEN_FILE} " f"environment variable." ) self._path = resolved @property def path(self) -> pathlib.Path: return self._path def __call__(self) -> str: try: content = self._path.read_text(encoding="utf-8").strip() except FileNotFoundError as err: raise AnthropicError(f"Identity token file not found at {self._path}.") from err except PermissionError as err: raise AnthropicError( f"Identity token file at {self._path} is not readable by this process: {err}. " f"Check the file mode and the effective uid of the process." ) from err except IsADirectoryError as err: raise AnthropicError( f"Identity token path {self._path} is a directory, not a file. " f"Point at the projected token file itself." ) from err except (OSError, UnicodeDecodeError) as err: raise AnthropicError(f"Identity token file at {self._path} could not be read: {err}") from err if not content: raise AnthropicError( f"Identity token file at {self._path} is empty. " f"If this is a Kubernetes projected service-account token, check the " f"volume mount and the serviceAccountToken projection audience." ) return content class InMemoryConfig(CredentialsFile): """An :class:`AccessTokenProvider` driven by an in-memory config dict (same shape as ``configs/.json``) rather than files on disk. Intended for callers that want to construct an :class:`anthropic.Anthropic` client with a fully programmatic credentials setup — equivalent to the Go SDK's ``option.WithConfig`` / TypeScript SDK's ``ClientOptions.config``. Both ``authentication.type`` discriminator values are supported: ``"oidc_federation"`` ``authentication.credentials_path`` is **optional**. If set, exchanged tokens are cached to / read from that file (same atomic 0600 write as :class:`CredentialsFile`). If omitted, every call performs a fresh jwt-bearer exchange with no on-disk cache. ``"user_oauth"`` ``authentication.credentials_path`` is **required** — it is where the access/refresh tokens live. Behaviour is identical to a file-backed :class:`CredentialsFile` profile of the same shape. The implementation subclasses :class:`CredentialsFile` so the dispatch, refresh-grant, disk-cache and atomic-write logic are shared verbatim; only config loading and identity-token resolution are overridden. """ _IN_MEMORY_PATH = pathlib.Path("") def __init__( self, config: Dict[str, Any], *, identity_token_provider: Optional[IdentityTokenProvider] = None, http_client: Optional[httpx.Client] = None, ) -> None: raw_auth = config.get("authentication") if not isinstance(raw_auth, dict): raise AnthropicError( "config dict is missing the 'authentication' object. " f'Expected shape: {{"authentication": {{"type": "{AUTH_TYPE_OIDC_FEDERATION}"' f'|"{AUTH_TYPE_USER_OAUTH}", ...}}, ...}}' ) auth = cast("Dict[str, Any]", raw_auth) auth_type = auth.get("type") if auth_type not in (AUTH_TYPE_OIDC_FEDERATION, AUTH_TYPE_USER_OAUTH): raise AnthropicError( f"Unknown authentication.type {auth_type!r}. " f"Expected {AUTH_TYPE_OIDC_FEDERATION!r} or {AUTH_TYPE_USER_OAUTH!r}." ) credentials_path = auth.get("credentials_path") if auth_type == AUTH_TYPE_USER_OAUTH and not credentials_path: raise AnthropicError( f"authentication.type {AUTH_TYPE_USER_OAUTH!r} requires " f"'authentication.credentials_path' (where the access/refresh tokens live). " f"For profile-based resolution, use CredentialsFile instead." ) # CredentialsFile state — set directly rather than calling super().__init__() # because the parent constructor reads env/disk for profile resolution. self._profile = "" self._config_path = self._IN_MEMORY_PATH self._bound_base_url: Optional[str] = None self._http_client = http_client self._owned_http_client: Optional[httpx.Client] = None self._workload_delegate: Optional[WorkloadIdentityCredentials] = None self._identity_token_provider_override = identity_token_provider self._config = config self._credentials_path = pathlib.Path(str(credentials_path)).expanduser() if credentials_path else None self._base_url = self._resolve_base_url(config) _require_https(self._base_url, field="config: base_url") @override def _load_config(self) -> Dict[str, Any]: assert self._config is not None return self._config @override def reload(self) -> None: # Config is fixed at construction; only drop the workload delegate so # a re-exchange picks up rotated identity-token state. self._workload_delegate = None @override def _build_workload_delegate(self, auth: Dict[str, Any]) -> WorkloadIdentityCredentials: if self._identity_token_provider_override is None: return super()._build_workload_delegate(auth) from ._workload import WorkloadIdentityError, WorkloadIdentityCredentials federation_rule_id = auth.get("federation_rule_id") assert self._config is not None organization_id = self._config.get("organization_id") if not federation_rule_id or not organization_id: raise WorkloadIdentityError( f"config dict with authentication.type {AUTH_TYPE_OIDC_FEDERATION!r} must include " f"'authentication.federation_rule_id' and top-level 'organization_id'" ) delegate = WorkloadIdentityCredentials( identity_token_provider=self._identity_token_provider_override, federation_rule_id=federation_rule_id, organization_id=organization_id, service_account_id=auth.get("service_account_id"), workspace_id=self._config.get("workspace_id"), scope=auth.get("scope"), http_client=self._get_http_client(), ) delegate.bind_base_url(self._base_url) return delegate anthropic-sdk-python-0.120.2/src/anthropic/lib/credentials/_secrets.py000066400000000000000000000103171523216435200257500ustar00rootroot00000000000000from __future__ import annotations import json from typing import Any, Dict, Optional, cast from pydantic import SecretStr __all__ = [ "SecretStr", "_NonObjectPayloadError", "_wrap_secret_fields", "_unwrap_secret", "_json_dumps_secrets", "_strip_traceback", ] class _NonObjectPayloadError(TypeError): """Raised by :func:`_wrap_secret_fields` for JSON payloads that are not objects. Carries only the payload's type name — a non-object payload can be an echo of the request (assertion included), so it must never bind in a caller's frame or ride along in an exception. """ def __init__(self, type_name: str) -> None: super().__init__(f"expected a JSON object, got {type_name}") self.type_name = type_name # Top-level keys that are plumbing, not secrets, in the credentials file and # in token-endpoint responses. Every OTHER string value is treated as secret # by default, so new fields (id_token, client_secret, ...) are wrapped without # anyone having to remember to list them. _PLAIN_KEYS = frozenset( ( # credentials file "type", "version", "expires_at", # RFC 6749 token / error response "token_type", "expires_in", "scope", "error", "error_description", "error_uri", ) ) def _wrap_secret_fields(payload: Any) -> Dict[str, Any]: """Wrap the secret fields of a parsed JSON object in ``SecretStr``. Called at every boundary where credential material enters SDK code. Traceback frames retain their locals, so any dict a raise site (or a frame an error merely propagates through) still holds must already be redacted — ``SecretStr`` renders as ``SecretStr('**********')`` under crash reporters that capture and render locals. String values are secret unless their key is in ``_PLAIN_KEYS``; wrapped empty strings stay falsy (``SecretStr`` defines ``__len__`` across the supported pydantic range), so ``if not creds.get("access_token")`` checks behave unchanged. Only top-level values are wrapped — the credential formats are flat; revisit if a nested shape ever appears. Mutates ``payload`` in place — a copy would leave the raw-valued original reachable — and returns it. Non-object payloads raise :class:`_NonObjectPayloadError` from this frame, with the payload unbound first, so the raw value never lands in any frame of the traceback — callers translate to their own redacted error. """ if not isinstance(payload, dict): type_name = type(payload).__name__ del payload raise _NonObjectPayloadError(type_name) mapping = cast("Dict[str, Any]", payload) for key in mapping: if key not in _PLAIN_KEYS and isinstance(mapping.get(key), str): mapping[key] = SecretStr(mapping[key]) return mapping def _unwrap_secret(value: Any) -> Any: """Inverse of :func:`_wrap_secret_fields` for a single value; pass-through for values that were never wrapped (absent or non-string fields).""" return value.get_secret_value() if isinstance(value, SecretStr) else value def _json_default(value: Any) -> Any: if isinstance(value, SecretStr): return value.get_secret_value() raise TypeError(f"Object of type {type(value).__name__} is not JSON serializable") def _json_dumps_secrets(payload: Any, *, indent: Optional[int] = None) -> bytes: """``json.dumps`` with ``SecretStr`` values unwrapped at dump time. Returns bytes so call sites can pass the result inline (request content, ``os.write``) without binding the raw serialization to a local. """ return json.dumps(payload, indent=indent, default=_json_default).encode("utf-8") def _strip_traceback(err: BaseException) -> BaseException: """Detach the frames chained onto ``err`` before raising from it. Foreign frames (json decoder, httpx transport) hold raw payloads — request bodies, response text, credentials-file contents — as locals. Dropping the traceback removes them from every renderer and programmatic chain-walker, while the cause's type and message (which never carry the payload) stay visible in renderings. """ err.__traceback__ = None return err anthropic-sdk-python-0.120.2/src/anthropic/lib/credentials/_types.py000066400000000000000000000060071523216435200254450ustar00rootroot00000000000000from __future__ import annotations from typing import Dict, Callable, Optional, Protocol from dataclasses import field, dataclass from typing_extensions import override def _empty_headers() -> Dict[str, str]: return {} __all__ = ["AccessToken", "AccessTokenProvider", "IdentityTokenProvider", "CredentialResult"] @dataclass(frozen=True) class AccessToken: """An Anthropic API access token with optional expiry. ``expires_at`` is unix seconds; ``None`` means no expiry information (the token will be treated as never-expires by :class:`TokenCache`). ``repr()`` masks the token (at most its last four characters) so a frame or log line holding an ``AccessToken`` never exposes the raw value — crash reporters that capture traceback locals render their ``repr``. """ token: str expires_at: Optional[int] = None @override def __repr__(self) -> str: # str() first: a malformed token endpoint can hand us a non-str token # and a repr must never raise (crash reporters call it blindly). token = str(self.token) masked = f"...{token[-4:]}" if len(token) >= 12 else "**********" return f"AccessToken(token='{masked}', expires_at={self.expires_at!r})" class AccessTokenProvider(Protocol): """Callable that mints or returns a cached access token. Re-invoking the provider IS the refresh mechanism — providers have no separate ``refresh()`` method. Providers may be stateful (hold config / paths) but the *cache* lives in :class:`TokenCache`, not here. The optional ``force_refresh`` flag is set by :meth:`TokenCache.invalidate` after a 401: providers with on-disk caches (user_oauth, oidc_federation) must bypass their freshness short-circuit and always fetch fresh when it is True. Providers without a cache can accept and ignore the flag. """ def __call__(self, *, force_refresh: bool = False) -> AccessToken: ... # Innermost layer: returns the raw external JWT string (used as the # ``identity_token_provider`` argument to :class:`WorkloadIdentityCredentials`). IdentityTokenProvider = Callable[[], str] @dataclass(frozen=True) class CredentialResult: """Bundles an :class:`AccessTokenProvider` with config-level metadata. Returned by :func:`default_credentials`. The ``extra_headers`` dict carries headers that should be set on every API request (e.g. ``anthropic-workspace-id``). The client merges these into its default headers at construction time. ``base_url`` is the API host the resolved profile is configured for (e.g. a staging endpoint). The client adopts it as its request ``base_url`` *only* when the user did not supply one explicitly via the ``base_url=`` kwarg or ``ANTHROPIC_BASE_URL`` — see the constructor in ``_client.py``. ``None`` means the profile did not specify a host and the client keeps its own default. """ provider: AccessTokenProvider extra_headers: Dict[str, str] = field(default_factory=_empty_headers) base_url: Optional[str] = None anthropic-sdk-python-0.120.2/src/anthropic/lib/credentials/_workload.py000066400000000000000000000370221523216435200261240ustar00rootroot00000000000000from __future__ import annotations import time import logging from types import TracebackType from typing import Any, Dict, Type, Union, NoReturn, Optional from typing_extensions import override import httpx from ._types import AccessToken, IdentityTokenProvider from ._secrets import ( SecretStr, _unwrap_secret, _strip_traceback, _json_dumps_secrets, _wrap_secret_fields, _NonObjectPayloadError, ) from ._constants import ( TOKEN_ENDPOINT, DEFAULT_BASE_URL, GRANT_TYPE_JWT_BEARER, OAUTH_API_BETA_HEADER, FEDERATION_BETA_HEADER, TOKEN_EXCHANGE_TIMEOUT, _user_agent, _require_https, ) from ..._exceptions import AnthropicError # jwt-bearer POSTs require BOTH beta headers — oauth-2025-04-20 unlocks the # token endpoint family, and oidc-federation-2026-04-01 routes the POST to the # Go userauth handler rather than the Python oauth_server. _JWT_BEARER_BETA_HEADER = f"{OAUTH_API_BETA_HEADER},{FEDERATION_BETA_HEADER}" # Max characters of response body kept on WorkloadIdentityError.body and in # exception messages. Token endpoints sometimes echo back the assertion JWT or # other sensitive material on error; truncating limits the blast radius if the # exception ends up in user logs or crash reports. _MAX_ERROR_BODY_CHARS = 256 # Hard limits on the wire size of the assertion JWT we send and the response # body we accept from the token endpoint. JWTs from real IdPs are <4 KiB; a # 16 KiB ceiling catches misconfiguration (e.g. a PEM cert path passed as the # token) before we POST it. The 1 MiB response cap bounds memory if a misrouted # endpoint streams back something pathological. _MAX_ASSERTION_BYTES = 16 * 1024 _MAX_TOKEN_RESPONSE_BYTES = 1 << 20 def _request_id(resp: httpx.Response) -> Optional[str]: rid: Optional[str] = resp.headers.get("Request-Id") or resp.headers.get("request-id") return rid def _redact_body(body: Any) -> Any: """Truncate a token-endpoint error body for safe inclusion in an exception.""" if body is None: return None if isinstance(body, str): if len(body) <= _MAX_ERROR_BODY_CHARS: return body return body[:_MAX_ERROR_BODY_CHARS] + f"... <{len(body) - _MAX_ERROR_BODY_CHARS} more chars>" # For dict payloads, only keep OAuth standard error fields (RFC 6749 §5.2). if isinstance(body, dict): kept: Dict[str, Any] = {} for key in ("error", "error_description", "error_uri"): if key in body: kept[key] = body[key] return kept return None def _raise_token_endpoint_error(resp: httpx.Response, *, message_prefix: str, hint: Optional[str] = None) -> NoReturn: """Raise a redacted :class:`WorkloadIdentityError` from a non-200 token-endpoint response. Shared between the jwt-bearer exchange path in this module and the refresh_token grant path in :mod:`_providers`. The raw response body (which token endpoints can echo credential material into) is never bound to a local in this frame — only the redaction is — so this frame is safe under crash reporters that capture traceback locals. ``hint`` is an optional caller-supplied diagnostic appended verbatim to the error message (after the redacted body). Callers gate it on the response status and their own state — this helper does not inspect ``resp`` for it. """ try: redacted = _redact_body(resp.json()) except ValueError: redacted = _redact_body(resp.text) message = f"{message_prefix} (HTTP {resp.status_code}): {redacted}" if hint: message = f"{message} {hint}" raise WorkloadIdentityError( message, status_code=resp.status_code, body=redacted, request_id=_request_id(resp), ) __all__ = ["WorkloadIdentityCredentials", "WorkloadIdentityError", "exchange_federation_assertion"] log: logging.Logger = logging.getLogger(__name__) class WorkloadIdentityError(AnthropicError): """Raised when the OIDC token exchange (``POST /v1/oauth/token``) fails.""" status_code: Optional[int] body: Any request_id: Optional[str] def __init__( self, message: str, *, status_code: Optional[int] = None, body: Any = None, request_id: Optional[str] = None, ) -> None: super().__init__(message) self.status_code = status_code self.body = body self.request_id = request_id @override def __str__(self) -> str: base = super().__str__() if self.request_id: return f"{base} [request_id={self.request_id}]" return base class WorkloadIdentityCredentials: """Exchanges an external OIDC JWT for an Anthropic access token via the RFC 7523 ``jwt-bearer`` grant. This is an :class:`AccessTokenProvider`: calling it performs a *fresh* token exchange. Wrap in a :class:`TokenCache` (done automatically when passed as ``credentials=`` to :class:`anthropic.Anthropic`) to avoid exchanging on every request. Args: organization_id: The organization's raw UUID string (organizations do not use tagged IDs). workspace_id: Optional ``wrkspc_*`` tagged ID, or the literal ``"default"`` to scope the token to the organization's default workspace. When omitted the server picks the rule's sole enabled workspace, else the org default if the rule covers it. Required when the rule enables more than one non-default workspace, or to target a specific workspace other than the one the server would pick. The minted token is workspace-scoped: per-request workspace selection (the ``anthropic-workspace-id`` header) is not supported for federation tokens — switching workspaces requires a new token exchange with a different ``workspace_id``. """ def __init__( self, *, identity_token_provider: IdentityTokenProvider, federation_rule_id: str, organization_id: str, service_account_id: Optional[str] = None, workspace_id: Optional[str] = None, scope: Optional[str] = None, http_client: Optional[httpx.Client] = None, ) -> None: self._identity_token_provider = identity_token_provider self._federation_rule_id = federation_rule_id self._organization_id = organization_id self._service_account_id = service_account_id self._workspace_id = workspace_id # Scope is informational only for federation: the server derives the # effective scope from the matching federation rule and the gateway # transform drops unknown body fields, so it is intentionally NOT sent # on the jwt-bearer request. self._scope = scope # The client passing this object as ``credentials=`` calls # :meth:`bind_base_url` to set its own endpoint, so the token exchange # and the API calls hit the same deployment. There is intentionally no # constructor kwarg for this: a token minted by one deployment is only # valid against that deployment, so splitting exchange-base from # client-base is always a bug. self._bound_base_url: Optional[str] = None if http_client is None: self._http_client = httpx.Client(timeout=TOKEN_EXCHANGE_TIMEOUT) self._owns_http_client = True else: self._http_client = http_client self._owns_http_client = False @property def scope(self) -> Optional[str]: return self._scope @property def _base_url(self) -> str: return self._bound_base_url or DEFAULT_BASE_URL def bind_base_url(self, base_url: str) -> None: """Set the API ``base_url`` the token exchange POSTs to. Called by :class:`anthropic.Anthropic` when this object is passed as ``credentials=``, so callers don't pass the same URL twice. For standalone use (no client) or tests, call this directly. """ bound = base_url.rstrip("/") _require_https(bound, field="base_url") self._bound_base_url = bound def close(self) -> None: """Close the underlying ``httpx.Client`` if we created it.""" if self._owns_http_client: self._http_client.close() def __enter__(self) -> "WorkloadIdentityCredentials": return self def __exit__( self, exc_type: Optional[Type[BaseException]], exc: Optional[BaseException], tb: Optional[TracebackType], ) -> None: self.close() def __call__(self, *, force_refresh: bool = False) -> AccessToken: # Re-invoke the identity token provider every time — the underlying # file (e.g. a k8s projected SA token) may have rotated. force_refresh # is a no-op: this provider has no cache to bypass. del force_refresh jwt = SecretStr(self._identity_token_provider()) assertion_bytes = len(jwt.get_secret_value().encode("utf-8")) if assertion_bytes > _MAX_ASSERTION_BYTES: raise WorkloadIdentityError( f"Identity token assertion is {assertion_bytes} bytes, which exceeds the " f"{_MAX_ASSERTION_BYTES}-byte limit. This is almost certainly not a JWT — check " f"that the identity-token path points at the projected token, not a key or cert." ) body: Dict[str, Union[str, SecretStr]] = { "grant_type": GRANT_TYPE_JWT_BEARER, "assertion": jwt, "federation_rule_id": self._federation_rule_id, "organization_id": self._organization_id, } if self._service_account_id is not None: body["service_account_id"] = self._service_account_id if self._workspace_id is not None: body["workspace_id"] = self._workspace_id url = f"{self._base_url}{TOKEN_ENDPOINT}" try: resp = self._http_client.post( url, # Serialized inline so the raw request bytes are never bound # to a local here; SecretStr values unwrap at dump time. content=_json_dumps_secrets(body), headers={ "anthropic-beta": _JWT_BEARER_BETA_HEADER, "Content-Type": "application/json", "User-Agent": _user_agent(), }, ) except httpx.HTTPError as err: raise WorkloadIdentityError(f"Failed to reach token endpoint {url}: {err}") from _strip_traceback(err) request_id = _request_id(resp) if len(resp.content) > _MAX_TOKEN_RESPONSE_BYTES: raise WorkloadIdentityError( f"Token endpoint response body exceeds {_MAX_TOKEN_RESPONSE_BYTES} bytes " f"(got {len(resp.content)}); refusing to parse.", status_code=resp.status_code, request_id=request_id, ) if resp.status_code >= 400: # A 401 is almost always a federation-rule mismatch. Point at the # rule and the Console auth-event log; when the caller hasn't pinned # a workspace, also surface the multi-workspace fix rather than # making them dig through docs. hint: Optional[str] = None if resp.status_code == 401: hint = "Ensure your federation rule matches your identity token. " if self._workspace_id is None: hint += ( "If your federation rule is scoped to multiple workspaces, set the " "ANTHROPIC_WORKSPACE_ID environment variable, the 'workspace_id' " "config key, or the workspace_id= argument. " ) hint += ( "View your authentication events in the Workload identity page of Claude Console for more details." ) _raise_token_endpoint_error(resp, message_prefix="Token exchange failed", hint=hint) try: # Token values are SecretStr-wrapped in place at the parse # boundary, so every error path below may hold ``data`` in its # frame without retaining raw credential material. data = _wrap_secret_fields(resp.json()) except ValueError as err: redacted = _redact_body(resp.text) raise WorkloadIdentityError( f"Token endpoint returned non-JSON response (status {resp.status_code}): {redacted}", status_code=resp.status_code, body=redacted, request_id=request_id, ) from _strip_traceback(err) except _NonObjectPayloadError as err: # A non-object payload can be an echo of the request (assertion # included), so it is rejected inside the helper — no frame in # this traceback holds it — and only its type name is reported. raise WorkloadIdentityError( f"Token endpoint returned a JSON {err.type_name} (status {resp.status_code}); expected an object.", status_code=resp.status_code, request_id=request_id, ) from None token_type = data.get("token_type") if token_type is not None and str(token_type).lower() != "bearer": raise WorkloadIdentityError( f"Token endpoint returned unsupported token_type {token_type!r} (expected 'Bearer').", status_code=resp.status_code, body=_redact_body(data), request_id=request_id, ) try: token = data["access_token"] # ``expires_in`` is a JSON number per RFC 6749 §5.1; coerce to int seconds. expires_in = int(data["expires_in"]) except (KeyError, TypeError, ValueError) as err: raise WorkloadIdentityError( "Token endpoint response missing required fields (access_token / expires_in).", status_code=resp.status_code, body=_redact_body(data), request_id=request_id, ) from err return AccessToken(token=_unwrap_secret(token), expires_at=int(time.time()) + expires_in) def exchange_federation_assertion( *, assertion: Union[str, SecretStr], federation_rule_id: str, organization_id: str, service_account_id: Optional[str] = None, workspace_id: Optional[str] = None, base_url: Optional[str] = None, http_client: Optional[httpx.Client] = None, ) -> AccessToken: """Perform a single RFC 7523 ``jwt-bearer`` exchange and return the resulting :class:`AccessToken`. This is a one-shot convenience wrapper around :class:`WorkloadIdentityCredentials` for callers that already have the assertion JWT in hand and just want the Anthropic access token back (no caching, no provider plumbing). ``assertion`` may be a :class:`pydantic.SecretStr` to keep it redacted end-to-end; a plain ``str`` is wrapped on entry. """ if isinstance(assertion, str): # Rebind so this frame's local holds the wrapped form — the raw string # then lives only in the caller's frame, which no callee can scrub. assertion = SecretStr(assertion) creds = WorkloadIdentityCredentials( identity_token_provider=assertion.get_secret_value, federation_rule_id=federation_rule_id, organization_id=organization_id, service_account_id=service_account_id, workspace_id=workspace_id, http_client=http_client, ) if base_url is not None: creds.bind_base_url(base_url) try: return creds() finally: creds.close() anthropic-sdk-python-0.120.2/src/anthropic/lib/environments/000077500000000000000000000000001523216435200240175ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/lib/environments/__init__.py000066400000000000000000000040611523216435200261310ustar00rootroot00000000000000"""Self-hosted environment runner helpers. - :func:`anthropic.resources.beta.environments.work.AsyncWork.poller` (``client.beta.environments.work.poller(...)``) — control-plane only: claims work items, ack's each one, and hands back the work item. Async only (lives on ``AsyncWork``, not the sync ``Work``). The underlying generators are :func:`iter_work` / :func:`aiter_work`. - :class:`SessionToolRunner` (``client.beta.sessions.events.tool_runner(...)``) — the sessions-side counterpart to ``client.beta.messages.tool_runner``: dispatches local tools against a session's ``agent.tool_use`` events. - :class:`EnvironmentWorker` (``client.beta.environments.work.worker(...)``) — the full composition: poll → set up the workdir + download the session agent's skills → run a :class:`SessionToolRunner` while heartbeating the work-item lease → force-stop on exit → loop. Build it with ``client.beta.environments.work.worker(...)`` or construct it directly: ``EnvironmentWorker(client, ...)``; use :meth:`EnvironmentWorker.handle_item` for the per-item flow when you already hold a claimed work item. The tool implementations themselves (:func:`beta_agent_toolset` and the per-tool factories) live next to the other tool helpers — import them from ``anthropic.lib.tools.agent_toolset``. """ from ._poller import ( POLL_BLOCK_MS, iter_work, aiter_work, ) from ._worker import EnvironmentWorker, EnvironmentWorkerTools from ..tools._skills import download_session_skills from ..tools._beta_session_runner import ( DEFAULT_MAX_IDLE, MANAGED_AGENTS_BETA, SessionToolRunner, DispatchedToolCall, BetaAnyRunnableTool, DispatchedToolUseEvent, DispatchedToolResultParams, ) __all__ = [ "iter_work", "aiter_work", "POLL_BLOCK_MS", "EnvironmentWorker", "EnvironmentWorkerTools", "SessionToolRunner", "DispatchedToolCall", "DispatchedToolUseEvent", "DispatchedToolResultParams", "BetaAnyRunnableTool", "download_session_skills", "MANAGED_AGENTS_BETA", "DEFAULT_MAX_IDLE", ] anthropic-sdk-python-0.120.2/src/anthropic/lib/environments/_poller.py000066400000000000000000000257241523216435200260370ustar00rootroot00000000000000from __future__ import annotations import time import socket import logging from uuid import uuid4 from collections.abc import Iterator, AsyncIterator import anyio from .._retry import TRANSIENT_ERRORS, jitter, backoff, is_fatal_status_error from ..._types import Headers, omit from ..._exceptions import APIStatusError from ...types.beta.environments import BetaSelfHostedWork from ...resources.beta.environments.work import Work, AsyncWork __all__ = [ "iter_work", "aiter_work", "POLL_BLOCK_MS", ] # API caps block_ms at 999; rely on client-side jitter between empty polls. POLL_BLOCK_MS = 999 _POLL_BACKOFF_CAP = 60.0 log = logging.getLogger(__name__) def _backoff(attempt: int) -> float: return backoff(attempt, cap=_POLL_BACKOFF_CAP) def _jitter(low: float, high: float) -> float: return jitter(low, high) def _is_fatal_4xx(err: Exception) -> bool: return is_fatal_status_error(err) def _is_status(err: Exception, code: int) -> bool: return isinstance(err, APIStatusError) and err.status_code == code def _default_worker_id() -> str: # The API documents anthropic_worker_id as a *unique* id, and multiple # workers can share a host, so the hostname alone is not enough — suffix it # with a uuid4 so each process gets a distinct, still-readable id. return f"{socket.gethostname()}-{uuid4().hex[:12]}" def iter_work( work: Work, *, environment_id: str, worker_id: str | None = None, block_ms: int | None = POLL_BLOCK_MS, reclaim_older_than_ms: int | None = None, drain: bool = False, auto_stop: bool = True, extra_headers: Headers | None = None, ) -> Iterator[BetaSelfHostedWork]: """Iterate work items claimed from a self-hosted environment. Each yielded :class:`BetaSelfHostedWork` has already been ack'd. The ``work`` resource must be bound to a client authenticated for the environment — the poller itself does not handle credentials. Use ``client.beta.environments.work.poller(...)`` for the user-facing entry point that constructs a scoped sub-client for you. Two consumption shapes are supported: - **Long-running runner** (``drain=False, auto_stop=True``, the default): loops forever, sleeps with jitter on empty polls, and calls ``work.stop`` when the consuming for-loop body returns or raises. The poller owns the whole work-item lifecycle. - **Drain-and-dispatch** (``drain=True, auto_stop=False``): returns as soon as the queue is empty and never calls ``work.stop`` — use this when each yielded item is handed off to another process (e.g. a webhook handler that spawns a sandbox per work item) and that process owns ``stop``. Args: block_ms: How long the server holds an empty poll open (long-poll). Pass ``None`` to omit the param for a non-blocking poll — the server rejects ``0``. Drain callers usually want ``None`` so the final empty poll returns immediately. drain: When True, return after the first empty poll instead of sleeping and re-polling. Lets a webhook-driven dispatcher drain the queue and respond. auto_stop: When True (default), call ``work.stop`` after the consumer's loop body completes. Set False when the work item is handed off to another process that owns the stop call — otherwise the lease is terminated out from under it. reclaim_older_than_ms: Forwarded to ``work.poll``. Reclaim un-ack'd work older than this many ms. Useful in drain mode so a dead runner's work re-surfaces on the next webhook delivery. extra_headers: Optional headers passed through per request on every poll / ack / stop call (including the force-stop of an unprocessable item). They are threaded into each call's ``extra_headers=`` and are never assigned onto the client, so client state is not mutated. Credentials and ``x-stainless-helper`` come from the bound client, not this argument; a header given here overrides the bound client's same-named default for that one request, so use it for caller passthrough (e.g. trace ids), not to set auth. """ worker_id = worker_id or _default_worker_id() log.info("poller starting environment_id=%s drain=%s auto_stop=%s", environment_id, drain, auto_stop) # Poll and ack each get their own backoff counter so a run of ack failures # can't inflate the next poll failure's backoff (and vice versa) — each is # reset on its own success, and the ``continue`` paths leave them untouched. poll_attempt = 0 ack_attempt = 0 while True: try: item = work.poll( environment_id, block_ms=block_ms if block_ms is not None else omit, reclaim_older_than_ms=reclaim_older_than_ms if reclaim_older_than_ms is not None else omit, anthropic_worker_id=worker_id, extra_headers=extra_headers, ) except TRANSIENT_ERRORS as e: if _is_fatal_4xx(e): log.error("poll failed permanently error=%s", e) raise poll_attempt += 1 wait = _backoff(poll_attempt) + _jitter(0.0, 1.0) log.warning("poll failed attempt=%d backoff=%.1fs error=%s", poll_attempt, wait, e) time.sleep(wait) continue poll_attempt = 0 if item is None: if drain: log.info("queue drained environment_id=%s", environment_id) return time.sleep(_jitter(1.0, 3.0)) continue log.info("claimed work work_id=%s work_type=%s", item.id, getattr(item.data, "type", None)) try: work.ack( item.id, environment_id=environment_id, extra_headers=extra_headers, ) except TRANSIENT_ERRORS as e: if _is_fatal_4xx(e): log.error("ack failed permanently; force-stopping work_id=%s error=%s", item.id, e) _force_stop_quietly(work, item.id, environment_id=environment_id, extra_headers=extra_headers) continue ack_attempt += 1 wait = _backoff(ack_attempt) + _jitter(0.0, 1.0) log.warning( "ack failed, backing off work_id=%s attempt=%d backoff=%.1fs error=%s", item.id, ack_attempt, wait, e ) time.sleep(wait) continue ack_attempt = 0 if not auto_stop: yield item continue try: yield item finally: try: work.stop( item.id, environment_id=environment_id, extra_headers=extra_headers, ) except Exception as e: if not _is_status(e, 409): log.warning("stop failed work_id=%s error=%s", item.id, e) def _force_stop_quietly(work: Work, work_id: str, *, environment_id: str, extra_headers: Headers | None = None) -> None: """Best-effort ``work.stop(force=True)`` for an item that can't be processed. A 409 just means the work already stopped; anything else is logged but not raised, since the poll loop must keep going regardless. """ try: work.stop(work_id, environment_id=environment_id, force=True, extra_headers=extra_headers) except Exception as e: if not _is_status(e, 409): log.error("force-stop of unprocessable work failed work_id=%s error=%s", work_id, e) async def aiter_work( work: AsyncWork, *, environment_id: str, worker_id: str | None = None, block_ms: int | None = POLL_BLOCK_MS, reclaim_older_than_ms: int | None = None, drain: bool = False, auto_stop: bool = True, extra_headers: Headers | None = None, ) -> AsyncIterator[BetaSelfHostedWork]: """Async version of :func:`iter_work`. See its docstring for semantics, including how ``extra_headers`` is passed through per request. """ worker_id = worker_id or _default_worker_id() log.info("poller starting environment_id=%s drain=%s auto_stop=%s", environment_id, drain, auto_stop) poll_attempt = 0 ack_attempt = 0 while True: try: item = await work.poll( environment_id, block_ms=block_ms if block_ms is not None else omit, reclaim_older_than_ms=reclaim_older_than_ms if reclaim_older_than_ms is not None else omit, anthropic_worker_id=worker_id, extra_headers=extra_headers, ) except TRANSIENT_ERRORS as e: if _is_fatal_4xx(e): log.error("poll failed permanently error=%s", e) raise poll_attempt += 1 wait = _backoff(poll_attempt) + _jitter(0.0, 1.0) log.warning("poll failed attempt=%d backoff=%.1fs error=%s", poll_attempt, wait, e) await anyio.sleep(wait) continue poll_attempt = 0 if item is None: if drain: log.info("queue drained environment_id=%s", environment_id) return await anyio.sleep(_jitter(1.0, 3.0)) continue log.info("claimed work work_id=%s work_type=%s", item.id, getattr(item.data, "type", None)) try: await work.ack( item.id, environment_id=environment_id, extra_headers=extra_headers, ) except TRANSIENT_ERRORS as e: if _is_fatal_4xx(e): log.error("ack failed permanently; force-stopping work_id=%s error=%s", item.id, e) await _aforce_stop_quietly(work, item.id, environment_id=environment_id, extra_headers=extra_headers) continue ack_attempt += 1 wait = _backoff(ack_attempt) + _jitter(0.0, 1.0) log.warning( "ack failed, backing off work_id=%s attempt=%d backoff=%.1fs error=%s", item.id, ack_attempt, wait, e ) await anyio.sleep(wait) continue ack_attempt = 0 if not auto_stop: yield item continue try: yield item finally: try: await work.stop( item.id, environment_id=environment_id, extra_headers=extra_headers, ) except Exception as e: if not _is_status(e, 409): log.warning("stop failed work_id=%s error=%s", item.id, e) async def _aforce_stop_quietly( work: AsyncWork, work_id: str, *, environment_id: str, extra_headers: Headers | None = None ) -> None: """Async version of :func:`_force_stop_quietly`.""" try: await work.stop(work_id, environment_id=environment_id, force=True, extra_headers=extra_headers) except Exception as e: if not _is_status(e, 409): log.error("force-stop of unprocessable work failed work_id=%s error=%s", work_id, e) anthropic-sdk-python-0.120.2/src/anthropic/lib/environments/_worker.py000066400000000000000000000551621523216435200260520ustar00rootroot00000000000000"""The self-hosted environment worker — the full composition of the control-plane poller and the per-session tool runner. :class:`EnvironmentWorker` claims work items from a self-hosted environment, and for each claimed ``session`` work item: builds the per-session :class:`~anthropic.lib.tools.agent_toolset.AgentToolContext` and downloads the session agent's skills, then runs a :class:`~anthropic.lib.tools._beta_session_runner.SessionToolRunner` for the session *while* heartbeating the work-item lease in parallel; on exit it force-stops the work item and loops to the next one. The lease heartbeat reporting ``state == "stopping"`` (or a lost lease) ends the session run. Build one from the generated work resource:: client.beta.environments.work.worker(environment_id=..., environment_key=...) or construct it directly:: from anthropic.lib.environments import EnvironmentWorker EnvironmentWorker(client, environment_id=..., environment_key=...) :meth:`EnvironmentWorker.handle_item` runs that same per-work-item flow for a single work item you've already claimed (e.g. a ``worker poll --on-work`` script handed one to a fresh process); with no arguments it reads the ``ANTHROPIC_*`` env vars that command sets. """ from __future__ import annotations import os import time import logging from typing import TYPE_CHECKING, Union, Callable from collections.abc import Sequence import anyio from .._retry import TRANSIENT_ERRORS from ._poller import _is_status, aiter_work, _is_fatal_4xx from ..._types import Headers, NotGiven, not_given from .._scoped_client import _copy_client_with_bearer_auth from ...types.beta.environments import BetaSelfHostedWork, BetaSessionWorkData from ..tools._beta_session_runner import ( DEFAULT_MAX_IDLE, BetaAnyRunnableTool, _run_session_tools, ) if TYPE_CHECKING: from ..._client import AsyncAnthropic from ..tools.agent_toolset import AgentToolContext from ...resources.beta.environments.work import AsyncWork # ``agent_toolset`` pulls in host-only modules (``subprocess``, ``tarfile``, …), # so it is never imported at module level here — only as a type above, and # lazily for its values inside ``_tools_for`` / ``_handle_item``. That keeps this # module host-dep-free so the generated ``work`` resource can expose # ``EnvironmentWorker`` without dragging those imports into ``import anthropic``. __all__ = ["EnvironmentWorker", "EnvironmentWorkerTools"] log = logging.getLogger(__name__) _HEARTBEAT_DEFAULT = 30.0 # Assumed lease TTL before the server's first heartbeat response tells us the # real value — used to decide when a run of transient failures means the lease # is gone. _HEARTBEAT_TTL_DEFAULT = 90.0 _NO_HEARTBEAT_SENTINEL = "NO_HEARTBEAT" # A fixed tool list, or a factory invoked once per claimed session with that # session's ``AgentToolContext`` — use the factory form to bind # :func:`beta_agent_toolset_20260401` (or any tool that needs the workdir / # session id) to the right session. EnvironmentWorkerTools = Union[ Sequence[BetaAnyRunnableTool], Callable[["AgentToolContext"], Sequence[BetaAnyRunnableTool]] ] # Transient errors the heartbeat loop retries on top of ``TRANSIENT_ERRORS``: # ``anyio.fail_after`` (which bounds each heartbeat) raises the builtin # ``TimeoutError`` rather than an ``APIError``, so it would otherwise fall # through to the un-retried branch. Declared at module level with an explicit # type so mypy can verify the ``except`` clause; an inline # ``except (*TRANSIENT_ERRORS, TimeoutError)`` types as ``tuple[Any, ...]`` # and mypy rejects it as not-an-exception-tuple. _HEARTBEAT_TRANSIENT_ERRORS: tuple[type[Exception], ...] = (*TRANSIENT_ERRORS, TimeoutError) async def _heartbeat_loop( work: AsyncWork, *, work_id: str, environment_id: str, stop: anyio.Event, extra_headers: Headers | None = None, ) -> None: """Keep the work-item lease alive while a session is being served. ``work`` must be bound to a sub-client authenticated for the environment; this loop adds no auth of its own. Sets ``stop`` when the control plane reports the work is ``stopping`` / ``stopped``, when the lease is no longer extended, on a permanent heartbeat failure, or when transient failures have run long enough that the lease must be assumed lost (so two runners don't end up serving the same work). """ interval = _HEARTBEAT_DEFAULT ttl = _HEARTBEAT_TTL_DEFAULT last = _NO_HEARTBEAT_SENTINEL last_success = time.monotonic() while not stop.is_set(): try: # Bound each heartbeat: a network blackhole must not leave us # awaiting for the SDK's multi-minute default while the lease TTL # (tens of seconds) expires out from under us. with anyio.fail_after(interval): resp = await work.heartbeat( work_id, environment_id=environment_id, expected_last_heartbeat=last, extra_headers=extra_headers, ) # Anything outside ``_HEARTBEAT_TRANSIENT_ERRORS`` is a real bug and # propagates rather than being swallowed and retried until the lease # is assumed lost. except _HEARTBEAT_TRANSIENT_ERRORS as e: if _is_fatal_4xx(e): log.error("permanent heartbeat failure error=%s", e) stop.set() return # A transient failure (5xx, timeout, connection error) is not a 4xx, # so retrying forever risks split-brain once the lease expires. If no # heartbeat has succeeded within the lease TTL, assume it's lost. if time.monotonic() - last_success > ttl: log.error("lease assumed lost: no successful heartbeat in %.0fs error=%s", ttl, e) stop.set() return log.warning("transient heartbeat failure error=%s", e) else: last = resp.last_heartbeat last_success = time.monotonic() if resp.ttl_seconds > 0: ttl = resp.ttl_seconds interval = max(1.0, min(resp.ttl_seconds / 2, _HEARTBEAT_DEFAULT)) if resp.state in ("stopping", "stopped") or not resp.lease_extended: log.info("heartbeat signals shutdown state=%s lease_extended=%s", resp.state, resp.lease_extended) stop.set() return # Sleep up to `interval` seconds, but wake immediately if stop is set. with anyio.move_on_after(interval): await stop.wait() def _require(value: str | None, *, name: str, env_var: str) -> str: """Fall back to ``env_var`` for ``value``; raise a clear error if still empty. The ``ANTHROPIC_*`` env vars are the ones the ``ant worker poll --on-work`` command sets on the process it spawns for a claimed work item. """ resolved = value or os.environ.get(env_var) if not resolved: raise ValueError(f"handle_item: {name} is required — pass it or set {env_var}") return resolved class EnvironmentWorker: """Run a self-hosted environment worker. Composed from the control-plane poller (``client.beta.environments.work.poller``) and the per-session :class:`SessionToolRunner`. For each claimed ``session`` work item it builds the per-session :class:`AgentToolContext` and downloads the session agent's skills, then runs a session tool runner for the session *while* heartbeating the work-item lease in parallel; on exit it force-stops the work item and loops to the next one. A single ``environment_key`` is the worker's only credential: a Bearer-only scoped sub-client is built once per call (one for polling, one for heartbeat / force-stop, and the session tool runner builds its own internally), so every request the worker issues is authenticated by the environment key with the parent client's ``X-Api-Key`` cleared. Async only — :meth:`run` loops forever, so bound it (cancel the task or wrap it in :func:`asyncio.wait_for`) when you want it to stop. Use :meth:`handle_item` if you already hold a claimed work item (e.g. a ``worker poll --on-work`` script handed one to a fresh process) and just want the per-item flow without the poll loop — with no arguments it reads the ``ANTHROPIC_*`` env vars that command sets, so ``environment_id`` (only used by :meth:`run`) isn't needed. Prefer ``client.beta.environments.work.worker(...)`` to build one; the direct constructor below is equivalent. Example:: from anthropic import AsyncAnthropic client = AsyncAnthropic() # Long-running daemon: poll for work, serve each session, loop. await client.beta.environments.work.worker( environment_id=environment_id, environment_key=environment_key, workdir="/workspace", ).run() # Already-claimed item (e.g. inside `ant worker poll --on-work ...`): await client.beta.environments.work.worker(workdir="/workspace").handle_item() # Equivalent, constructing the worker directly: from anthropic.lib.environments import EnvironmentWorker await EnvironmentWorker(client, workdir="/workspace").handle_item() Args: client: The async Anthropic client. environment_id: The self-hosted environment to poll for work. Required by :meth:`run`; not used by :meth:`handle_item`. environment_key: The environment key — the worker's single credential. Used as the Bearer credential on the scoped sub-clients the worker constructs for the control-plane (poll / ack / stop) and session-level (events stream / list / send + heartbeat / force-stop) calls. Required by :meth:`run`; :meth:`handle_item` falls back to it (then to ``ANTHROPIC_ENVIRONMENT_KEY``) when not passed one. tools: Tools to expose to each claimed session. Either a fixed list, or a factory invoked once per session with that session's :class:`AgentToolContext`. Defaults to ``beta_agent_toolset_20260401(env)`` (the standard ``agent_toolset_20260401`` set bound to the per-session context). workdir: Base directory for the per-session :class:`AgentToolContext`. Defaults to :func:`os.getcwd` captured when the worker is constructed (matches the TS worker's ``process.cwd()``-at-construction), so a ``chdir`` between constructing the worker and serving a session does not change where tools resolve paths. unrestricted_paths: Forwarded to the per-session :class:`AgentToolContext`. max_idle: Forwarded to the session tool runner — seconds to keep running after the session goes idle with ``stop_reason`` ``end_turn``. Defaults to :data:`~anthropic.lib.environments.DEFAULT_MAX_IDLE` (60s). ``None`` disables it. worker_id: Optional identifier sent on each poll. Defaults to a unique, hostname-prefixed id. extra_headers: Optional headers passed through per request on every call the worker makes (poll / ack / stop / heartbeat and the session tool runner's event stream / list / send). They are threaded into each call's ``extra_headers=`` and never assigned onto the client, so client state is not mutated. Auth and ``x-stainless-helper`` are supplied by the worker's scoped sub-clients (and the parent client's ``default_headers`` propagate via their ``client.copy()``); a header given here overrides a scoped client's same-named default for that request, so use it for caller passthrough (e.g. trace ids), not auth. """ def __init__( self, client: AsyncAnthropic, *, environment_id: str | None = None, environment_key: str | None = None, tools: EnvironmentWorkerTools | None = None, workdir: str | os.PathLike[str] | None = None, unrestricted_paths: bool = False, max_file_bytes: int | None | NotGiven = not_given, max_idle: float | None = DEFAULT_MAX_IDLE, worker_id: str | None = None, extra_headers: Headers | None = None, ) -> None: self._client = client self._environment_id = environment_id self._environment_key = environment_key self._tools = tools # Snapshot the cwd at construction time when no explicit workdir was # given (TS parity: ``process.cwd()`` captured up front). Resolving "." # lazily at first tool use would instead pick up any intervening chdir. self._workdir: str | os.PathLike[str] = os.getcwd() if workdir is None else workdir self._unrestricted_paths = unrestricted_paths self._max_file_bytes = max_file_bytes self._max_idle = max_idle self._worker_id = worker_id self._extra_headers = extra_headers def _tools_for(self, env: AgentToolContext) -> Sequence[BetaAnyRunnableTool]: if callable(self._tools): return self._tools(env) if self._tools is not None: return self._tools # Lazy import: keeps the host-only ``agent_toolset`` module out of this # module's import graph (see the note next to the imports). from ..tools.agent_toolset import beta_agent_toolset_20260401 return beta_agent_toolset_20260401(env) async def run(self) -> None: """Poll the environment and service each claimed session until cancelled. Loops forever; cancel the task (or wrap it in :func:`asyncio.wait_for`) to stop it. Equivalent to claiming work items via ``client.beta.environments.work.poller`` and running the per-item flow for each. Raises: ValueError: if ``environment_id`` / ``environment_key`` were not passed to the constructor. """ environment_id = self._environment_id environment_key = self._environment_key if environment_id is None or environment_key is None: raise ValueError("EnvironmentWorker.run: environment_id and environment_key are required to poll for work") # Poll/ack/stop calls run through a Bearer-only sub-client tagged with # the poller's helper telemetry. ``_handle_item`` builds its own # ``environments-worker``-tagged sub-client for the heartbeat / force-stop. poll_client = _copy_client_with_bearer_auth( self._client, auth_token=environment_key, helper="environments-work-poller" ) async for work_item in aiter_work( poll_client.beta.environments.work, environment_id=environment_id, worker_id=self._worker_id, auto_stop=False, extra_headers=self._extra_headers, ): await self._handle_item(work_item, environment_key) async def handle_item( self, *, work_id: str | None = None, environment_id: str | None = None, session_id: str | None = None, environment_key: str | None = None, ) -> None: """Service a single, already-claimed work item without the poll loop. Builds the per-session :class:`AgentToolContext` (workdir from this worker's options) and downloads the session agent's skills, then runs a :class:`SessionToolRunner` for the session *while* heartbeating the work-item lease in parallel, and force-stops the work item on exit (whether the runner finishes normally, raises, or the heartbeat loop signals shutdown). Use this when something else does the claiming — e.g. a ``worker poll --on-work`` script that hands an already-claimed item to a fresh process. ``work_id`` / ``environment_id`` / ``session_id`` fall back to ``ANTHROPIC_WORK_ID`` / ``ANTHROPIC_ENVIRONMENT_ID`` / ``ANTHROPIC_SESSION_ID`` (the env vars that command sets) when not passed; ``environment_key`` resolves in order: the explicit argument, then this worker's own ``environment_key``, then ``ANTHROPIC_ENVIRONMENT_KEY`` — so with no arguments inside that command it just works. Non-session work items are ignored (but still force-stopped so the lease doesn't sit until TTL). Raises: ValueError: if any of ``work_id`` / ``environment_id`` / ``session_id`` / ``environment_key`` is still empty after the fallbacks. """ work_id = _require(work_id, name="work_id", env_var="ANTHROPIC_WORK_ID") environment_id = _require(environment_id, name="environment_id", env_var="ANTHROPIC_ENVIRONMENT_ID") session_id = _require(session_id, name="session_id", env_var="ANTHROPIC_SESSION_ID") # environment_key resolves: explicit arg -> this worker's own key -> # ANTHROPIC_ENVIRONMENT_KEY -> a clear "required" error. environment_key = _require( environment_key or self._environment_key, name="environment_key", env_var="ANTHROPIC_ENVIRONMENT_KEY", ) # The per-item flow only reads work.id / work.environment_id / # work.data.type / work.data.id, so a minimally populated model is # enough. work_item = BetaSelfHostedWork.model_construct( id=work_id, environment_id=environment_id, data=BetaSessionWorkData.model_construct(type="session", id=session_id), ) await self._handle_item(work_item, environment_key) async def _handle_item(self, work_item: BetaSelfHostedWork, environment_key: str) -> None: """The per-item body shared by :meth:`run`'s poll loop and :meth:`handle_item`. Runs a :class:`SessionToolRunner` for the work item's session while heartbeating its lease, force-stopping the work item on exit. All control-plane traffic for this work item — heartbeat + force-stop — flows through a Bearer-only sub-client built here; the session tool runner builds its own ``session-tool-runner``-tagged sub-client internally. """ # Lazy import: keeps the host-only ``agent_toolset`` module out of this # module's import graph (see the note next to the imports). from ..tools.agent_toolset import AgentToolContext # ``environments-worker``-scoped sub-client for the heartbeat and # force-stop calls this item drives. The session tool runner is given # the parent client + environment_key and builds its own sub-client. worker_client = _copy_client_with_bearer_auth( self._client, auth_token=environment_key, helper="environments-worker" ) work_res = worker_client.beta.environments.work try: session_id = work_item.data.id async with anyio.create_task_group() as tg: stop = anyio.Event() async def _heartbeat( work_id: str = work_item.id, environment_id: str = work_item.environment_id, stop_ev: anyio.Event = stop, ) -> None: try: await _heartbeat_loop( work_res, work_id=work_id, environment_id=environment_id, stop=stop_ev, extra_headers=self._extra_headers, ) finally: tg.cancel_scope.cancel() # Start the lease heartbeat BEFORE entering AgentToolContext. # AgentToolContext.__aenter__ downloads and extracts every skill # the session agent has; that can take longer than the lease # TTL. If the first heartbeat only fired *after* the download # (the old ordering), a slow download would let the lease lapse # and another worker reclaim the item — both workers then serve # the same session (split-brain). Heartbeating concurrently with # the download keeps the lease ours the entire time. The # heartbeat only needs work_id / environment_id, both available # before any download. tg.start_soon(_heartbeat) # Drive AgentToolContext's enter/exit explicitly rather than via # ``async with`` so its async cleanup (bash subprocess teardown # + downloaded-skill removal) runs *shielded*: by the time we # tear down, the heartbeat may have cancelled the task-group # scope (lost lease), and that cancel must not abort the # subprocess kill / skill rmtree. A heartbeat-driven cancel # during __aenter__ still interrupts an in-progress skill # download (the desired split-brain protection) — __aexit__ is # then a no-op since no bash/skills were set up. env = AgentToolContext( workdir=self._workdir, unrestricted_paths=self._unrestricted_paths, max_file_bytes=self._max_file_bytes, client=worker_client, session_id=session_id, ) try: await env.__aenter__() tools = self._tools_for(env) try: async with _run_session_tools( self._client, session_id, tools=tools, max_idle=self._max_idle, environment_key=environment_key, extra_headers=self._extra_headers, ) as calls: async for _ in calls: pass finally: stop.set() tg.cancel_scope.cancel() finally: with anyio.CancelScope(shield=True): await env.__aexit__(None, None, None) finally: # Best-effort: force-stop the work item so the lease doesn't sit # until TTL expiry. Idempotent server-side; a 409 just means the # work already stopped. Shielded so the post survives any # surrounding cancellation. with anyio.CancelScope(shield=True): try: await work_res.stop( work_item.id, environment_id=work_item.environment_id, force=True, extra_headers=self._extra_headers, ) except Exception as e: if not _is_status(e, 409): log.error("force-stop on exit failed work_id=%s error=%s", work_item.id, e) anthropic-sdk-python-0.120.2/src/anthropic/lib/foundry.md000066400000000000000000000052021523216435200232770ustar00rootroot00000000000000# Anthropic Foundry To use this library with Foundry, use the `AnthropicFoundry` class instead of the `Anthropic` class. ## Installation ```bash pip install anthropic ``` ## Usage ### Basic Usage with API Key ```python from anthropic import AnthropicFoundry client = AnthropicFoundry( api_key="...", # defaults to ANTHROPIC_FOUNDRY_API_KEY environment variable resource="my-resource", # your Foundry resource ) message = client.messages.create( model="claude-sonnet-5", max_tokens=1024, messages=[{"role": "user", "content": "Hello!"}], ) print(message.content[0].text) ``` ### Using Azure AD Token Provider For enhanced security, you can use Azure AD (Microsoft Entra) authentication instead of an API key: ```python from anthropic import AnthropicFoundry from azure.identity import DefaultAzureCredential from azure.identity import get_bearer_token_provider credential = DefaultAzureCredential() token_provider = get_bearer_token_provider( credential, "https://ai.azure.com/.default" ) client = AnthropicFoundry( azure_ad_token_provider=token_provider, resource="my-resource", ) message = client.messages.create( model="claude-sonnet-5", max_tokens=1024, messages=[{"role": "user", "content": "Hello!"}], ) print(message.content[0].text) ``` ## Examples ### Streaming Messages ```python from anthropic import AnthropicFoundry client = AnthropicFoundry( api_key="...", resource="my-resource", ) with client.messages.stream( model="claude-sonnet-5", max_tokens=1024, messages=[{"role": "user", "content": "Write a haiku about programming"}], ) as stream: for text in stream.text_stream: print(text, end="", flush=True) ``` ### Async Usage ```python from anthropic import AsyncAnthropicFoundry async def main(): client = AsyncAnthropicFoundry( api_key="...", resource="my-resource", ) message = await client.messages.create( model="claude-sonnet-5", max_tokens=1024, messages=[{"role": "user", "content": "Hello!"}], ) print(message.content[0].text) import asyncio asyncio.run(main()) ``` ### Async Streaming ```python from anthropic import AsyncAnthropicFoundry async def main(): client = AsyncAnthropicFoundry( api_key="...", resource="my-resource", ) async with client.messages.stream( model="claude-sonnet-5", max_tokens=1024, messages=[{"role": "user", "content": "Write a haiku about programming"}], ) as stream: async for text in stream.text_stream: print(text, end="", flush=True) import asyncio asyncio.run(main()) ```anthropic-sdk-python-0.120.2/src/anthropic/lib/foundry.py000066400000000000000000000542141523216435200233360ustar00rootroot00000000000000from __future__ import annotations import os import inspect from typing import Any, Union, Mapping, TypeVar, Callable, Sequence, Awaitable, cast, overload from functools import cached_property from typing_extensions import Self, override import httpx from .._types import NOT_GIVEN, Omit, Headers, Timeout, NotGiven from .._utils import is_given from .._client import Anthropic, AsyncAnthropic from .._compat import model_copy from .._models import FinalRequestOptions from .._streaming import Stream, AsyncStream from .._exceptions import AnthropicError from .._middleware import MiddlewareInput from .._base_client import ( DEFAULT_MAX_RETRIES, BaseClient, merge_headers, ) from ..resources.beta import Beta, AsyncBeta from ..resources.messages import Messages, AsyncMessages from ..resources.beta.messages import Messages as BetaMessages, AsyncMessages as AsyncBetaMessages AzureADTokenProvider = Callable[[], str] AsyncAzureADTokenProvider = Callable[[], "str | Awaitable[str]"] _HttpxClientT = TypeVar("_HttpxClientT", bound=Union[httpx.Client, httpx.AsyncClient]) _DefaultStreamT = TypeVar("_DefaultStreamT", bound=Union[Stream[Any], AsyncStream[Any]]) class MutuallyExclusiveAuthError(AnthropicError): def __init__(self) -> None: super().__init__( "The `api_key` and `azure_ad_token_provider` arguments are mutually exclusive; Only one can be passed at a time" ) class BaseFoundryClient(BaseClient[_HttpxClientT, _DefaultStreamT]): ... class MessagesFoundry(Messages): @cached_property @override def batches(self) -> None: # type: ignore[override] """Batches endpoint is not supported for Anthropic Foundry client.""" return None class BetaFoundryMessages(BetaMessages): @cached_property @override def batches(self) -> None: # type: ignore[override] """Batches endpoint is not supported for Anthropic Foundry client.""" return None class BetaFoundry(Beta): @cached_property @override def messages(self) -> BetaMessages: # type: ignore[override] """Return beta messages resource instance with excluded unsupported endpoints.""" return BetaFoundryMessages(self._client) class AsyncMessagesFoundry(AsyncMessages): @cached_property @override def batches(self) -> None: # type: ignore[override] """Batches endpoint is not supported for Anthropic Foundry client.""" return None class AsyncBetaFoundryMessages(AsyncBetaMessages): @cached_property @override def batches(self) -> None: # type: ignore[override] """Batches endpoint is not supported for Anthropic Foundry client.""" return None class AsyncBetaFoundry(AsyncBeta): @cached_property @override def messages(self) -> AsyncBetaMessages: # type: ignore[override] """Return beta messages resource instance with excluded unsupported endpoints.""" return AsyncBetaFoundryMessages(self._client) # ============================================================================== class AnthropicFoundry(BaseFoundryClient[httpx.Client, Stream[Any]], Anthropic): @overload def __init__( self, *, resource: str | None = None, api_key: str | None = None, azure_ad_token_provider: AzureADTokenProvider | None = None, webhook_key: str | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, http_client: httpx.Client | None = None, middleware: Sequence[MiddlewareInput] | None = None, _strict_response_validation: bool = False, ) -> None: ... @overload def __init__( self, *, base_url: str, api_key: str | None = None, azure_ad_token_provider: AzureADTokenProvider | None = None, webhook_key: str | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, http_client: httpx.Client | None = None, middleware: Sequence[MiddlewareInput] | None = None, _strict_response_validation: bool = False, ) -> None: ... def __init__( self, *, resource: str | None = None, api_key: str | None = None, azure_ad_token_provider: AzureADTokenProvider | None = None, webhook_key: str | None = None, base_url: str | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, http_client: httpx.Client | None = None, middleware: Sequence[MiddlewareInput] | None = None, _strict_response_validation: bool = False, ) -> None: """Construct a new synchronous Anthropic Foundry client instance. This automatically infers the following arguments from their corresponding environment variables if they are not provided: - `api_key` from `ANTHROPIC_FOUNDRY_API_KEY` - `resource` from `ANTHROPIC_FOUNDRY_RESOURCE` - `base_url` from `ANTHROPIC_FOUNDRY_BASE_URL` Args: resource: Your Foundry resource name, e.g. `example-resource` for `https://example-resource.services.ai.azure.com/anthropic/` azure_ad_token_provider: A function that returns an Azure Active Directory token, will be invoked on every request. """ api_key = api_key if api_key is not None else os.environ.get("ANTHROPIC_FOUNDRY_API_KEY") resource = resource if resource is not None else os.environ.get("ANTHROPIC_FOUNDRY_RESOURCE") base_url = base_url if base_url is not None else os.environ.get("ANTHROPIC_FOUNDRY_BASE_URL") if api_key is None and azure_ad_token_provider is None: raise AnthropicError( "Missing credentials. Please pass one of `api_key`, `azure_ad_token_provider`, or the `ANTHROPIC_FOUNDRY_API_KEY` environment variable." ) if base_url is None: if resource is None: raise ValueError( "Must provide one of the `base_url` or `resource` arguments, or the `ANTHROPIC_FOUNDRY_RESOURCE` environment variable" ) base_url = f"https://{resource}.services.ai.azure.com/anthropic/" elif resource is not None: raise ValueError("base_url and resource are mutually exclusive") super().__init__( api_key=api_key, webhook_key=webhook_key, base_url=base_url, timeout=timeout, max_retries=max_retries, default_headers=default_headers, default_query=default_query, http_client=http_client, middleware=middleware, _strict_response_validation=_strict_response_validation, ) self._azure_ad_token_provider = azure_ad_token_provider @cached_property @override def models(self) -> None: # type: ignore[override] """Models endpoint is not supported for Anthropic Foundry client.""" return None @cached_property @override def messages(self) -> MessagesFoundry: # type: ignore[override] """Return messages resource instance with excluded unsupported endpoints.""" return MessagesFoundry(client=self) @cached_property @override def beta(self) -> Beta: # type: ignore[override] """Return beta resource instance with excluded unsupported endpoints.""" return BetaFoundry(self) @override def copy( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] — subclass intentionally drops `credentials` & `auth_token` self, *, api_key: str | None = None, azure_ad_token_provider: AzureADTokenProvider | None = None, webhook_key: str | None = None, base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, http_client: httpx.Client | None = None, max_retries: int | NotGiven = NOT_GIVEN, default_headers: Mapping[str, str] | None = None, set_default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, set_default_query: Mapping[str, object] | None = None, middleware: Sequence[MiddlewareInput] | None | NotGiven = NOT_GIVEN, _extra_kwargs: Mapping[str, Any] = {}, ) -> Self: """ Create a new client instance re-using the same options given to the current client with optional overriding. """ if default_headers is not None and set_default_headers is not None: raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") if default_query is not None and set_default_query is not None: raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") headers = self._custom_headers if default_headers is not None: headers = merge_headers(headers, default_headers) elif set_default_headers is not None: headers = set_default_headers params = self._custom_query if default_query is not None: params = {**params, **default_query} elif set_default_query is not None: params = set_default_query return self.__class__( api_key=api_key or self.api_key, azure_ad_token_provider=azure_ad_token_provider or self._azure_ad_token_provider, webhook_key=webhook_key or self.webhook_key, base_url=str(base_url or self.base_url), timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client or self._client, max_retries=max_retries if is_given(max_retries) else self.max_retries, default_headers=headers, default_query=params, middleware=self._middleware if isinstance(middleware, NotGiven) else middleware, **_extra_kwargs, ) with_options = copy # type: ignore[assignment] def _get_azure_ad_token(self) -> str | None: provider = self._azure_ad_token_provider if provider is not None: token = provider() if not token or not isinstance(token, str): # pyright: ignore[reportUnnecessaryIsInstance] raise ValueError( f"Expected `azure_ad_token_provider` argument to return a string but it returned {token}", ) return token return None @override def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: headers: dict[str, str | Omit] = {**options.headers} if is_given(options.headers) else {} options = model_copy(options) options.headers = headers azure_ad_token = self._get_azure_ad_token() if azure_ad_token is not None: if headers.get("Authorization") is None: headers["Authorization"] = f"Bearer {azure_ad_token}" elif self.api_key is not None: # In this branch `self.api_key` is always the Foundry key (explicit or # ANTHROPIC_FOUNDRY_API_KEY) — with an Azure AD token provider configured # the branch above wins, so an environment `ANTHROPIC_API_KEY` can never # be sent here. The endpoint authenticates with `x-api-key`; `api-key` is # also sent for backwards compatibility. if headers.get("x-api-key") is None: headers["x-api-key"] = self.api_key if headers.get("api-key") is None: headers["api-key"] = self.api_key else: # should never be hit raise ValueError("Unable to handle auth") return options @property @override def auth_headers(self) -> dict[str, str]: # Auth is attached per-request in `_prepare_options` (`x-api-key`/`api-key` # headers for API-key auth, or a bearer `Authorization` header for the Azure AD # token provider). Emitting nothing here stops the base client from sending an # `X-Api-Key` derived from `self.api_key`: when only an Azure AD token # provider is configured, `self.api_key` can be populated from an # `ANTHROPIC_API_KEY` in the environment, which must not be sent to the # Foundry endpoint. return {} @override def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: # Foundry attaches its own auth header in `_prepare_options`, so the base # requirement that `X-Api-Key`/`Authorization` already be present does not apply. return class AsyncAnthropicFoundry(BaseFoundryClient[httpx.AsyncClient, AsyncStream[Any]], AsyncAnthropic): @overload def __init__( self, *, resource: str | None = None, api_key: str | None = None, azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, webhook_key: str | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, http_client: httpx.AsyncClient | None = None, middleware: Sequence[MiddlewareInput] | None = None, _strict_response_validation: bool = False, ) -> None: ... @overload def __init__( self, *, base_url: str, api_key: str | None = None, azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, webhook_key: str | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, http_client: httpx.AsyncClient | None = None, middleware: Sequence[MiddlewareInput] | None = None, _strict_response_validation: bool = False, ) -> None: ... def __init__( self, *, resource: str | None = None, api_key: str | None = None, azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, webhook_key: str | None = None, base_url: str | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, http_client: httpx.AsyncClient | None = None, middleware: Sequence[MiddlewareInput] | None = None, _strict_response_validation: bool = False, ) -> None: """Construct a new asynchronous Anthropic Foundry client instance. This automatically infers the following arguments from their corresponding environment variables if they are not provided: - `api_key` from `ANTHROPIC_FOUNDRY_API_KEY` - `resource` from `ANTHROPIC_FOUNDRY_RESOURCE` - `base_url` from `ANTHROPIC_FOUNDRY_BASE_URL` Args: resource: Your Foundry resource name, e.g. `example-resource` for `https://example-resource.services.ai.azure.com/anthropic/` azure_ad_token_provider: A function that returns an Azure Active Directory token, will be invoked on every request. """ api_key = api_key if api_key is not None else os.environ.get("ANTHROPIC_FOUNDRY_API_KEY") resource = resource if resource is not None else os.environ.get("ANTHROPIC_FOUNDRY_RESOURCE") base_url = base_url if base_url is not None else os.environ.get("ANTHROPIC_FOUNDRY_BASE_URL") if api_key is None and azure_ad_token_provider is None: raise AnthropicError( "Missing credentials. Please pass one of `api_key`, `azure_ad_token_provider`, or the `ANTHROPIC_FOUNDRY_API_KEY` environment variable." ) if base_url is None: if resource is None: raise ValueError( "Must provide one of the `base_url` or `resource` arguments, or the `ANTHROPIC_FOUNDRY_RESOURCE` environment variable" ) base_url = f"https://{resource}.services.ai.azure.com/anthropic/" elif resource is not None: raise ValueError("base_url and resource are mutually exclusive") super().__init__( api_key=api_key, webhook_key=webhook_key, base_url=base_url, timeout=timeout, max_retries=max_retries, default_headers=default_headers, default_query=default_query, http_client=http_client, middleware=middleware, _strict_response_validation=_strict_response_validation, ) self._azure_ad_token_provider = azure_ad_token_provider @cached_property @override def models(self) -> None: # type: ignore[override] """Models endpoint is not supported for Azure Anthropic client.""" return None @cached_property @override def messages(self) -> AsyncMessagesFoundry: # type: ignore[override] """Return messages resource instance with excluded unsupported endpoints.""" return AsyncMessagesFoundry(client=self) @cached_property @override def beta(self) -> AsyncBetaFoundry: # type: ignore[override] """Return beta resource instance with excluded unsupported endpoints.""" return AsyncBetaFoundry(client=self) @override def copy( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] — subclass intentionally drops `credentials` & `auth_token` self, *, api_key: str | None = None, azure_ad_token_provider: AsyncAzureADTokenProvider | None = None, webhook_key: str | None = None, base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, http_client: httpx.AsyncClient | None = None, max_retries: int | NotGiven = NOT_GIVEN, default_headers: Mapping[str, str] | None = None, set_default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, set_default_query: Mapping[str, object] | None = None, middleware: Sequence[MiddlewareInput] | None | NotGiven = NOT_GIVEN, _extra_kwargs: Mapping[str, Any] = {}, ) -> Self: """ Create a new client instance re-using the same options given to the current client with optional overriding. """ if default_headers is not None and set_default_headers is not None: raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") if default_query is not None and set_default_query is not None: raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") headers = self._custom_headers if default_headers is not None: headers = merge_headers(headers, default_headers) elif set_default_headers is not None: headers = set_default_headers params = self._custom_query if default_query is not None: params = {**params, **default_query} elif set_default_query is not None: params = set_default_query return self.__class__( api_key=api_key or self.api_key, azure_ad_token_provider=azure_ad_token_provider or self._azure_ad_token_provider, webhook_key=webhook_key or self.webhook_key, base_url=str(base_url or self.base_url), timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client or self._client, max_retries=max_retries if is_given(max_retries) else self.max_retries, default_headers=headers, default_query=params, middleware=self._middleware if isinstance(middleware, NotGiven) else middleware, **_extra_kwargs, ) with_options = copy # type: ignore[assignment] async def _get_azure_ad_token(self) -> str | None: provider = self._azure_ad_token_provider if provider is not None: token = provider() if inspect.isawaitable(token): token = await token if not token or not isinstance(cast(Any, token), str): raise ValueError( f"Expected `azure_ad_token_provider` argument to return a string but it returned {token}", ) return str(token) return None @override async def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: headers: dict[str, str | Omit] = {**options.headers} if is_given(options.headers) else {} options = model_copy(options) options.headers = headers azure_ad_token = await self._get_azure_ad_token() if azure_ad_token is not None: if headers.get("Authorization") is None: headers["Authorization"] = f"Bearer {azure_ad_token}" elif self.api_key is not None: # See AnthropicFoundry._prepare_options: `self.api_key` here is always the # Foundry key, never an environment `ANTHROPIC_API_KEY`. if headers.get("x-api-key") is None: headers["x-api-key"] = self.api_key if headers.get("api-key") is None: headers["api-key"] = self.api_key else: # should never be hit raise ValueError("Unable to handle auth") return options @property @override def auth_headers(self) -> dict[str, str]: # See AnthropicFoundry.auth_headers: prevents leaking an environment # ANTHROPIC_API_KEY as X-Api-Key to the Foundry endpoint. return {} @override def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: # Foundry attaches its own auth header in `_prepare_options`. return anthropic-sdk-python-0.120.2/src/anthropic/lib/google_cloud/000077500000000000000000000000001523216435200237325ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/lib/google_cloud/README.md000066400000000000000000000122771523216435200252220ustar00rootroot00000000000000# Claude Platform on Google Cloud `AnthropicGoogleCloud` is a client for the full Anthropic API — Messages, Models, Batches, Files, Admin, and every beta surface — served through Google Cloud. You authenticate with Google Cloud IAM credentials, billing flows through Google Cloud Marketplace, and model strings are the same first-party identifiers used with the `Anthropic` client. The deprecated Completions endpoint is not exposed. This client never reads `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, or `ANTHROPIC_BASE_URL`; it authenticates with Google credentials only. > **Note:** the class name and `ANTHROPIC_GOOGLE_CLOUD_*` environment variable > names are provisional and may change before a stable release. ## Installation Google auth support is an optional dependency: ```bash pip install "anthropic[google_cloud]" ``` ## Authentication Precedence (first match wins, unless `skip_auth=True`): 1. `token_provider` — a callable returning a GCP access token, invoked on every request (so it can refresh internally). On the async client it may be async or return an awaitable; sync providers are run off the event loop. 2. `credentials` — a `google.auth` Credentials object, refreshed as needed. 3. Application Default Credentials — discovered via `google.auth.default()`, loaded lazily on the first request and cached for the life of the client. ADC covers `gcloud auth application-default login`, `GOOGLE_APPLICATION_CREDENTIALS`, workload identity on GKE/Cloud Run, and the GCE/Cloud Functions metadata server. A `workspace_id` is required unless `skip_auth=True` with an explicit `base_url`. `skip_auth` is mutually exclusive with the credential arguments. ## Usage ### Application Default Credentials (recommended) Run `gcloud auth application-default login` first, then: ```python from anthropic import AnthropicGoogleCloud client = AnthropicGoogleCloud( project="your-gcp-project", # or ANTHROPIC_GOOGLE_CLOUD_PROJECT location="us-central1", workspace_id="wrkspc_...", # or ANTHROPIC_GOOGLE_CLOUD_WORKSPACE_ID ) message = client.messages.create( model="claude-haiku-4-5", max_tokens=1024, messages=[{"role": "user", "content": "Hello!"}], ) print(message.content[0].text) ``` `project` may be omitted: it is taken from an explicit `credentials=` object when it exposes one (e.g. a service-account credential's `project_id`), or back-filled lazily on the first request from the project ADC resolves to (a service-account keyfile, the `GOOGLE_CLOUD_PROJECT` environment variable, or instance metadata). If no project can be resolved — plain user ADC, an explicit `token_provider`, or credentials without a project — the first request raises an error asking for `project`. ### Other credential modes
Explicit google.auth credentials ```python from google.oauth2 import service_account from anthropic import AnthropicGoogleCloud credentials = service_account.Credentials.from_service_account_file( "service-account.json", scopes=["https://www.googleapis.com/auth/cloud-platform"], ) client = AnthropicGoogleCloud( location="us-central1", workspace_id="wrkspc_...", credentials=credentials, # project= is taken from the service account when omitted ) ```
Custom token provider Use this when you already have a token-minting layer (a sidecar, a proxy, a broker) and don't want the SDK to talk to `google.auth` at all: ```python from anthropic import AnthropicGoogleCloud client = AnthropicGoogleCloud( project="your-gcp-project", location="us-central1", workspace_id="wrkspc_...", token_provider=lambda: my_token_broker.fetch(), # invoked on every request ) ``` On `AsyncAnthropicGoogleCloud` the provider may also be `async`.
### Streaming ```python with client.messages.stream( model="claude-haiku-4-5", max_tokens=1024, messages=[{"role": "user", "content": "Hello!"}], ) as stream: for text in stream.text_stream: print(text, end="", flush=True) ``` ### Async ```python from anthropic import AsyncAnthropicGoogleCloud client = AsyncAnthropicGoogleCloud( project="your-gcp-project", location="us-central1", workspace_id="wrkspc_..." ) message = await client.messages.create( model="claude-haiku-4-5", max_tokens=1024, messages=[{"role": "user", "content": "Hello!"}], ) async with client.messages.stream( model="claude-haiku-4-5", max_tokens=1024, messages=[{"role": "user", "content": "Hello!"}], ) as stream: async for text in stream.text_stream: print(text, end="", flush=True) ``` ## Configuration | Argument | Environment variable | Notes | |---|---|---| | `project` | `ANTHROPIC_GOOGLE_CLOUD_PROJECT` | Only needed when the base URL is derived; if omitted, back-filled from Google credentials on the first request. | | `location` | — | Required only when deriving the base URL. | | `workspace_id` | `ANTHROPIC_GOOGLE_CLOUD_WORKSPACE_ID` | Required unless `skip_auth=True` with an explicit `base_url`. | | `base_url` | `ANTHROPIC_GOOGLE_CLOUD_BASE_URL` | Overrides the derived gateway URL. | | `skip_auth` | — | For pre-authenticated proxies: skips token attachment and the workspace requirement. | anthropic-sdk-python-0.120.2/src/anthropic/lib/google_cloud/__init__.py000066400000000000000000000002071523216435200260420ustar00rootroot00000000000000from ._client import ( AnthropicGoogleCloud as AnthropicGoogleCloud, AsyncAnthropicGoogleCloud as AsyncAnthropicGoogleCloud, ) anthropic-sdk-python-0.120.2/src/anthropic/lib/google_cloud/_client.py000066400000000000000000000745301523216435200257320ustar00rootroot00000000000000from __future__ import annotations import os import inspect import threading from typing import TYPE_CHECKING, Any, Union, Mapping, TypeVar, Callable, Sequence, Awaitable, cast from functools import partial, cached_property from typing_extensions import Self, override import httpx from ..._types import NOT_GIVEN, Headers, Timeout, NotGiven from ..._utils import asyncify, is_given from ..._client import Anthropic, AsyncAnthropic from ..._models import FinalRequestOptions from ..._streaming import Stream, AsyncStream from ..._exceptions import AnthropicError from ..._middleware import MiddlewareInput from ..._base_client import DEFAULT_MAX_RETRIES, BaseClient, merge_headers from .._extras._google_auth import refresh_credentials, load_default_credentials # Bind the install-hint extra so a missing google-auth dep points users at # `pip install anthropic[google_cloud]` rather than the vertex extra. _load_adc_credentials = partial(load_default_credentials, extra="google_cloud") _refresh_credentials = partial(refresh_credentials, extra="google_cloud") if TYPE_CHECKING: from google.auth.credentials import Credentials as GoogleCredentials # type: ignore # The gateway base URL; stays overridable via the `base_url` argument and env var. DEFAULT_URL_TEMPLATE = ( "https://claude.googleapis.com/v1alpha/projects/{project}/locations/{location}/workspaces/{workspace_id}/invoke" ) # Used when no location is configured; the gateway should always be addressed # via the global region. DEFAULT_LOCATION = "global" TokenProvider = Callable[[], str] AsyncTokenProvider = Callable[[], "str | Awaitable[str]"] _HttpxClientT = TypeVar("_HttpxClientT", bound=Union[httpx.Client, httpx.AsyncClient]) _DefaultStreamT = TypeVar("_DefaultStreamT", bound=Union[Stream[Any], AsyncStream[Any]]) class _GoogleCredentialsState: """Holder for the Google credentials object, its refresh lock, and the project ADC resolved. Shared between a client and its ``copy()``/``with_options()`` clones (when the credential configuration is inherited) so a lazily-loaded ADC credential is minted once — not once per clone — and concurrent loads/refreshes are serialized: google-auth credential objects are not safe to ``refresh()`` concurrently. """ def __init__(self, credentials: GoogleCredentials | None) -> None: self._lock = threading.Lock() self.credentials: GoogleCredentials | None = credentials self.adc_project: str | None = None def token(self) -> str: """Return a valid access token, loading ADC / refreshing as needed. Blocking.""" with self._lock: if self.credentials is None: self.credentials, self.adc_project = _load_adc_credentials() elif self.credentials.expired or not self.credentials.token: _refresh_credentials(self.credentials) token = self.credentials.token if not token: raise AnthropicError("Could not resolve a GCP access token from the configured Google credentials") assert isinstance(token, str) return token class BaseGoogleCloudClient(BaseClient[_HttpxClientT, _DefaultStreamT]): """Marker base so ``_is_base_client()`` keeps these clients off the first-party credential-discovery chain (it matches only the exact ``Anthropic`` / ``AsyncAnthropic`` classes). Auth is handled entirely by this helper.""" workspace_id: str | None _project: str | None _location: str _creds_state: _GoogleCredentialsState _base_url_deferred: bool _base_url_overridden: bool @property @override def base_url(self) -> httpx.URL: return self._base_url @base_url.setter def base_url(self, url: httpx.URL | str) -> None: # An explicit post-construction assignment wins over (and cancels) the # pending project back-fill. self._base_url_deferred = False self._base_url = self._enforce_trailing_slash(url if isinstance(url, httpx.URL) else httpx.URL(url)) @property def google_credentials(self) -> GoogleCredentials | None: """The ``google.auth`` credentials in use (explicit or lazily-loaded ADC), if any. Distinct from ``.credentials``, which is the base client's first-party credentials provider and is always ``None`` on this client. """ return self._creds_state.credentials def _resolve_deferred_base_url(self) -> None: """Derive and set the real base URL once the project is known. Called on every request (after the project back-fill from ADC, if any); a no-op once the base URL has been derived. """ if not self._base_url_deferred: return if self._project is None: raise AnthropicError( "No `project` was provided and one could not be resolved from Google credentials. " "Pass the `project` argument, set the `ANTHROPIC_GOOGLE_CLOUD_PROJECT` " "environment variable, or provide `base_url` directly." ) # Deferred derivation implies auth, and constructing with auth requires a workspace ID. assert self.workspace_id is not None self.base_url = DEFAULT_URL_TEMPLATE.format( project=self._project, location=self._location, workspace_id=self.workspace_id ) self._base_url_deferred = False def _resolve_base_url( *, base_url: str | httpx.URL | None, project: str | None, location: str, workspace_id: str | None, allow_deferred_project: bool, ) -> str | httpx.URL | None: """base_url (arg or ``ANTHROPIC_GOOGLE_CLOUD_BASE_URL``, resolved by the caller) > derived template. Returns ``None`` when derivation must wait for the project to be back-filled from Google credentials on the first request (``allow_deferred_project``). """ if base_url is not None: return base_url # Derivation needs the workspace ID in the path; the constructors reject a # missing workspace before calling this without an explicit base_url. assert workspace_id is not None if project is None: if allow_deferred_project: return None raise ValueError( "No `project` was provided. Pass `project`, set the `ANTHROPIC_GOOGLE_CLOUD_PROJECT` " "environment variable, or provide `base_url` directly." ) return DEFAULT_URL_TEMPLATE.format(project=project, location=location, workspace_id=workspace_id) def _reject_skip_auth_conflict( *, skip_auth: bool, token_provider: object | None, credentials: object | None, ) -> None: if skip_auth and (token_provider is not None or credentials is not None): raise ValueError( "`skip_auth` is mutually exclusive with `token_provider` and `credentials`; " "`skip_auth` disables authentication entirely." ) def _project_from_credentials(credentials: GoogleCredentials) -> str | None: """Best-effort project from an explicit credentials object — service-account / impersonated credentials usually know theirs.""" for attr in ("project_id", "quota_project_id"): value = getattr(credentials, attr, None) if isinstance(value, str) and value: return value return None # ============================================================================== class AnthropicGoogleCloud(BaseGoogleCloudClient[httpx.Client, Stream[Any]], Anthropic): """Synchronous client for the first-party Anthropic API served through Google's gateway (Claude Platform on Google Cloud). The whole first-party surface is proxied verbatim (no URL or body rewriting), so this subclasses the full ``Anthropic`` client. Authentication is a GCP bearer token; the deprecated Completions endpoint is not exposed. """ workspace_id: str | None _skip_auth: bool def __init__( self, *, project: str | None = None, location: str | None = None, workspace_id: str | None = None, token_provider: TokenProvider | None = None, credentials: GoogleCredentials | None = None, skip_auth: bool = False, base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, http_client: httpx.Client | None = None, middleware: Sequence[MiddlewareInput] | None = None, _strict_response_validation: bool = False, ) -> None: """Construct a new synchronous Claude Platform on Google Cloud client. Auth precedence (first match wins, unless ``skip_auth=True``): 1. ``token_provider`` — a callable returning a GCP access token, invoked per request. 2. ``credentials`` — a ``google.auth`` Credentials object, refreshed as needed. 3. Application Default Credentials (``google.auth.default``). Args: project: GCP consumer project id (or ``ANTHROPIC_GOOGLE_CLOUD_PROJECT``, else ``GOOGLE_CLOUD_PROJECT``). Only needed when the base URL must be derived; if omitted there, it is taken from an explicit ``credentials`` object when it exposes one, or back-filled from ADC on the first request. location: GCP location (or ``ANTHROPIC_GOOGLE_CLOUD_LOCATION``). Optional — defaults to ``global``, the region the gateway should normally be addressed through. workspace_id: The Anthropic workspace ID (or ``ANTHROPIC_GOOGLE_CLOUD_WORKSPACE_ID``). Required unless ``skip_auth`` is set with an explicit ``base_url``. skip_auth: For pre-authenticated proxies — skips token attachment. A workspace ID is still needed to derive the base URL; pass ``base_url`` to construct without one. Mutually exclusive with the credential arguments. """ _reject_skip_auth_conflict(skip_auth=skip_auth, token_provider=token_provider, credentials=credentials) self._skip_auth = skip_auth self._token_provider = token_provider self._creds_state = _GoogleCredentialsState(credentials) if location is None: location = os.environ.get("ANTHROPIC_GOOGLE_CLOUD_LOCATION") if location is None: location = DEFAULT_LOCATION self._location = location if project is None: project = os.environ.get("ANTHROPIC_GOOGLE_CLOUD_PROJECT") if project is None: project = os.environ.get("GOOGLE_CLOUD_PROJECT") if project is None and credentials is not None: project = _project_from_credentials(credentials) self._project = project if base_url is None: base_url = os.environ.get("ANTHROPIC_GOOGLE_CLOUD_BASE_URL") # Distinguishes a user-supplied gateway URL from a template-derived one, so # `copy(project=..., location=...)` knows whether to re-derive. self._base_url_overridden = base_url is not None if workspace_id is None: workspace_id = os.environ.get("ANTHROPIC_GOOGLE_CLOUD_WORKSPACE_ID") # The workspace ID is required unless `skip_auth` is set together with an # explicit base URL — no URL to derive. if workspace_id is None and not (skip_auth and base_url is not None): raise ValueError( "No workspace ID found. Set the `workspace_id` argument or the " "`ANTHROPIC_GOOGLE_CLOUD_WORKSPACE_ID` environment variable." ) self.workspace_id = workspace_id resolved_base_url = _resolve_base_url( base_url=base_url, project=self._project, location=self._location, workspace_id=self.workspace_id, # Without auth there are no Google credentials to back-fill the project from. allow_deferred_project=not skip_auth, ) self._base_url_deferred = resolved_base_url is None super().__init__( # Deferred case: pass an empty (valid) URL so the parent doesn't fall through to # `ANTHROPIC_BASE_URL` / api.anthropic.com; `_prepare_options` derives the real # URL (or raises) before the first request is built. base_url=resolved_base_url if resolved_base_url is not None else "", timeout=timeout, max_retries=max_retries, default_headers=default_headers, default_query=default_query, http_client=http_client, middleware=middleware, _strict_response_validation=_strict_response_validation, ) # Never inherit first-party static credentials from the environment — the # base reads ANTHROPIC_API_KEY/ANTHROPIC_AUTH_TOKEN when no explicit # credential is passed, which would otherwise leak as `X-Api-Key` to the # gateway host. `auth_headers` is also overridden below as a hard guarantee. self.api_key = None self.auth_token = None @cached_property @override def completions(self) -> None: # type: ignore[override] """Completions endpoint is deprecated and not supported for the Google Cloud client.""" return None @property @override def auth_headers(self) -> dict[str, str]: # Auth is a GCP bearer token attached in `_prepare_request`; never emit # first-party `X-Api-Key` / `Authorization` headers from static credentials. return {} @override def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: # The bearer token is attached per-request in `_prepare_request`, not via # default headers, so the base auth-presence check would false-negative. return def _get_token(self) -> str: provider = self._token_provider if provider is not None: token = provider() if inspect.isawaitable(token): cast(Any, token).close() raise AnthropicError( "`token_provider` returned an awaitable. Async token providers are only " "supported on `AsyncAnthropicGoogleCloud`." ) return token return self._creds_state.token() @override def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: if not self._skip_auth and self._base_url_deferred: if self._project is None and self._token_provider is None: # An ADC load also resolves the credentials' project; do it here — # independent of token attachment — so the back-fill happens even # when the request carries its own `Authorization` header. self._creds_state.token() self._project = self._creds_state.adc_project self._resolve_deferred_base_url() return options @override def _prepare_request(self, request: httpx.Request) -> None: if self._skip_auth: return if request.headers.get("Authorization") is not None: # A caller-supplied Authorization header (per-request, default_headers, # or ANTHROPIC_CUSTOM_HEADERS) wins; the check is case-insensitive so # we never emit two conflicting Authorization headers. return request.headers["Authorization"] = f"Bearer {self._get_token()}" def copy( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] — subclass uses GCP auth self, *, project: str | None = None, location: str | None = None, workspace_id: str | None | NotGiven = NOT_GIVEN, token_provider: TokenProvider | None | NotGiven = NOT_GIVEN, credentials: GoogleCredentials | None | NotGiven = NOT_GIVEN, skip_auth: bool | None = None, base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, http_client: httpx.Client | None = None, middleware: Sequence[MiddlewareInput] | None | NotGiven = NOT_GIVEN, max_retries: int | NotGiven = NOT_GIVEN, default_headers: Mapping[str, str] | None = None, set_default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, set_default_query: Mapping[str, object] | None = None, _extra_kwargs: Mapping[str, Any] = {}, ) -> Self: """Create a new client re-using the current options, with optional overrides. Passing either of ``token_provider`` / ``credentials`` replaces the inherited credential configuration wholesale — the source not passed is cleared, so an explicit lower-precedence credential takes effect. ``workspace_id=None`` clears the workspace ID; ``project`` / ``location`` overrides re-derive a template-derived base URL. """ if default_headers is not None and set_default_headers is not None: raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") if default_query is not None and set_default_query is not None: raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") headers = self._custom_headers if default_headers is not None: headers = merge_headers(headers, default_headers) elif set_default_headers is not None: headers = set_default_headers params = self._custom_query if default_query is not None: params = {**params, **default_query} elif set_default_query is not None: params = set_default_query resolved_skip_auth = skip_auth if skip_auth is not None else self._skip_auth credential_overridden = is_given(token_provider) or is_given(credentials) new_token_provider: TokenProvider | None = None new_credentials: GoogleCredentials | None = None if credential_overridden: new_token_provider = token_provider if is_given(token_provider) else None new_credentials = credentials if is_given(credentials) else None elif not resolved_skip_auth: # don't round-trip credentials into a skip_auth clone new_token_provider = self._token_provider new_credentials = self._creds_state.credentials if base_url is None and not self._base_url_overridden: # The current URL is template-derived (or still pending); leave it unset # so __init__ re-derives from the new project/location. new_base_url: str | httpx.URL | None = None else: new_base_url = base_url if base_url is not None else self.base_url client = self.__class__( project=project if project is not None else self._project, location=location if location is not None else self._location, workspace_id=workspace_id if is_given(workspace_id) else self.workspace_id, token_provider=new_token_provider, credentials=new_credentials, skip_auth=resolved_skip_auth, base_url=new_base_url, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client or self._client, max_retries=max_retries if is_given(max_retries) else self.max_retries, default_headers=headers, default_query=params, middleware=self._middleware if isinstance(middleware, NotGiven) else middleware, _strict_response_validation=self._strict_response_validation, **_extra_kwargs, ) if not credential_overridden and not resolved_skip_auth: # Clones share lazily-loaded ADC credentials (and the refresh lock) so a # per-call `with_options()` clone doesn't mint its own token. client._creds_state = self._creds_state return client with_options = copy # type: ignore[assignment] class AsyncAnthropicGoogleCloud(BaseGoogleCloudClient[httpx.AsyncClient, AsyncStream[Any]], AsyncAnthropic): """Asynchronous client for the first-party Anthropic API served through Google's gateway (Claude Platform on Google Cloud). See ``AnthropicGoogleCloud``. """ workspace_id: str | None _skip_auth: bool def __init__( self, *, project: str | None = None, location: str | None = None, workspace_id: str | None = None, token_provider: AsyncTokenProvider | None = None, credentials: GoogleCredentials | None = None, skip_auth: bool = False, base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, http_client: httpx.AsyncClient | None = None, middleware: Sequence[MiddlewareInput] | None = None, _strict_response_validation: bool = False, ) -> None: """Construct a new asynchronous Claude Platform on Google Cloud client. ``token_provider`` may be sync or async; sync providers are run off the event loop. See ``AnthropicGoogleCloud`` for the full argument and auth-precedence docs. """ _reject_skip_auth_conflict(skip_auth=skip_auth, token_provider=token_provider, credentials=credentials) self._skip_auth = skip_auth self._token_provider = token_provider self._creds_state = _GoogleCredentialsState(credentials) if location is None: location = os.environ.get("ANTHROPIC_GOOGLE_CLOUD_LOCATION") if location is None: location = DEFAULT_LOCATION self._location = location if project is None: project = os.environ.get("ANTHROPIC_GOOGLE_CLOUD_PROJECT") if project is None: project = os.environ.get("GOOGLE_CLOUD_PROJECT") if project is None and credentials is not None: project = _project_from_credentials(credentials) self._project = project if base_url is None: base_url = os.environ.get("ANTHROPIC_GOOGLE_CLOUD_BASE_URL") self._base_url_overridden = base_url is not None if workspace_id is None: workspace_id = os.environ.get("ANTHROPIC_GOOGLE_CLOUD_WORKSPACE_ID") # The workspace ID is required unless `skip_auth` is set together with an # explicit base URL — no URL to derive. if workspace_id is None and not (skip_auth and base_url is not None): raise ValueError( "No workspace ID found. Set the `workspace_id` argument or the " "`ANTHROPIC_GOOGLE_CLOUD_WORKSPACE_ID` environment variable." ) self.workspace_id = workspace_id resolved_base_url = _resolve_base_url( base_url=base_url, project=self._project, location=self._location, workspace_id=self.workspace_id, # Without auth there are no Google credentials to back-fill the project from. allow_deferred_project=not skip_auth, ) self._base_url_deferred = resolved_base_url is None super().__init__( # Deferred case: pass an empty (valid) URL so the parent doesn't fall through to # `ANTHROPIC_BASE_URL` / api.anthropic.com; `_prepare_options` derives the real # URL (or raises) before the first request is built. base_url=resolved_base_url if resolved_base_url is not None else "", timeout=timeout, max_retries=max_retries, default_headers=default_headers, default_query=default_query, http_client=http_client, middleware=middleware, _strict_response_validation=_strict_response_validation, ) self.api_key = None self.auth_token = None @cached_property @override def completions(self) -> None: # type: ignore[override] """Completions endpoint is deprecated and not supported for the Google Cloud client.""" return None @property @override def auth_headers(self) -> dict[str, str]: return {} @override def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: return async def _get_token(self) -> str: provider = self._token_provider if provider is not None: if inspect.iscoroutinefunction(provider): token = await provider() assert isinstance(token, str) return token # A plain sync provider may block on a token mint; run it off # the event loop so concurrent requests aren't stalled. token = await asyncify(provider)() if inspect.isawaitable(token): token = await token assert isinstance(token, str) return token return await asyncify(self._creds_state.token)() @override async def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: if not self._skip_auth and self._base_url_deferred: if self._project is None and self._token_provider is None: # An ADC load also resolves the credentials' project; do it here — # independent of token attachment — so the back-fill happens even # when the request carries its own `Authorization` header. await asyncify(self._creds_state.token)() self._project = self._creds_state.adc_project self._resolve_deferred_base_url() return options @override async def _prepare_request(self, request: httpx.Request) -> None: if self._skip_auth: return if request.headers.get("Authorization") is not None: # A caller-supplied Authorization header (per-request, default_headers, # or ANTHROPIC_CUSTOM_HEADERS) wins; the check is case-insensitive so # we never emit two conflicting Authorization headers. return request.headers["Authorization"] = f"Bearer {await self._get_token()}" def copy( # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] — subclass uses GCP auth self, *, project: str | None = None, location: str | None = None, workspace_id: str | None | NotGiven = NOT_GIVEN, token_provider: AsyncTokenProvider | None | NotGiven = NOT_GIVEN, credentials: GoogleCredentials | None | NotGiven = NOT_GIVEN, skip_auth: bool | None = None, base_url: str | httpx.URL | None = None, timeout: float | Timeout | None | NotGiven = NOT_GIVEN, http_client: httpx.AsyncClient | None = None, middleware: Sequence[MiddlewareInput] | None | NotGiven = NOT_GIVEN, max_retries: int | NotGiven = NOT_GIVEN, default_headers: Mapping[str, str] | None = None, set_default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, set_default_query: Mapping[str, object] | None = None, _extra_kwargs: Mapping[str, Any] = {}, ) -> Self: """Create a new client re-using the current options, with optional overrides. See ``AnthropicGoogleCloud.copy`` for the override semantics. """ if default_headers is not None and set_default_headers is not None: raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") if default_query is not None and set_default_query is not None: raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") headers = self._custom_headers if default_headers is not None: headers = merge_headers(headers, default_headers) elif set_default_headers is not None: headers = set_default_headers params = self._custom_query if default_query is not None: params = {**params, **default_query} elif set_default_query is not None: params = set_default_query resolved_skip_auth = skip_auth if skip_auth is not None else self._skip_auth credential_overridden = is_given(token_provider) or is_given(credentials) new_token_provider: AsyncTokenProvider | None = None new_credentials: GoogleCredentials | None = None if credential_overridden: new_token_provider = token_provider if is_given(token_provider) else None new_credentials = credentials if is_given(credentials) else None elif not resolved_skip_auth: # don't round-trip credentials into a skip_auth clone new_token_provider = self._token_provider new_credentials = self._creds_state.credentials if base_url is None and not self._base_url_overridden: # The current URL is template-derived (or still pending); leave it unset # so __init__ re-derives from the new project/location. new_base_url: str | httpx.URL | None = None else: new_base_url = base_url if base_url is not None else self.base_url client = self.__class__( project=project if project is not None else self._project, location=location if location is not None else self._location, workspace_id=workspace_id if is_given(workspace_id) else self.workspace_id, token_provider=new_token_provider, credentials=new_credentials, skip_auth=resolved_skip_auth, base_url=new_base_url, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client or self._client, max_retries=max_retries if is_given(max_retries) else self.max_retries, default_headers=headers, default_query=params, middleware=self._middleware if isinstance(middleware, NotGiven) else middleware, _strict_response_validation=self._strict_response_validation, **_extra_kwargs, ) if not credential_overridden and not resolved_skip_auth: # Clones share lazily-loaded ADC credentials (and the refresh lock) so a # per-call `with_options()` clone doesn't mint its own token. client._creds_state = self._creds_state return client with_options = copy # type: ignore[assignment] anthropic-sdk-python-0.120.2/src/anthropic/lib/middleware/000077500000000000000000000000001523216435200234055ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/lib/middleware/__init__.py000066400000000000000000000002601523216435200255140ustar00rootroot00000000000000from ._fallbacks import ( DEFAULT_BETAS as DEFAULT_BETAS, BetaFallbackState as BetaFallbackState, BetaRefusalFallbackMiddleware as BetaRefusalFallbackMiddleware, ) anthropic-sdk-python-0.120.2/src/anthropic/lib/middleware/_fallbacks.py000066400000000000000000002112621523216435200260440ustar00rootroot00000000000000from __future__ import annotations import copy as _copy import json import logging from typing import ( Any, Dict, List, Callable, Iterable, Iterator, Optional, Generator, AsyncIterator, AsyncGenerator, cast, ) from contextvars import Token, ContextVar from typing_extensions import Literal, override import httpx from ..._utils import is_dict from ..._models import BaseModel from ..._request import APIRequest from ..._response import APIResponse, AsyncAPIResponse from ..._streaming import Stream, AsyncStream, ServerSentEvent from ..._exceptions import AnthropicError from ..._middleware import CallNext, Middleware, AsyncCallNext from ..._base_client import merge_headers from ...types.message import Message from .._stainless_helpers import helper_header from ...types.beta.beta_message import BetaMessage from ...types.anthropic_beta_param import AnthropicBetaParam from ...types.beta.beta_fallback_param import BetaFallbackParam from ...types.beta.beta_fallback_credit_token_param import BetaFallbackCreditTokenParam __all__ = [ "BetaFallbackState", "BetaRefusalFallbackMiddleware", ] # the documented logger name is the public package, not this private submodule log: logging.Logger = logging.getLogger("anthropic.lib.middleware") _MESSAGES_PATH = "/v1/messages" DEFAULT_BETAS: tuple[AnthropicBetaParam, ...] = ("fallback-credit-2026-07-01",) """Betas sent by default; override with the `betas` option.""" def _credit_token_param(token: str) -> BetaFallbackCreditTokenParam: """The retry's `fallback_credit_token`, in the object form. `best_effort` keeps the retry serving even when redemption fails — a bare string would 400 the hop on any token-layer failure. """ return {"token": token, "mode": "best_effort"} class BetaFallbackState: """Tracks which fallback a sequence of requests is pinned to. Create one and enter it (`with state:` — the same context manager works for both clients) around every request that should share the pin — the turns of one conversation, or any wider scope the stickiness should apply to; `BetaRefusalFallbackMiddleware` mutates it in place when a model refuses. """ index: int | None """Index into the fallback chain the requests are pinned to. `None` (or -1) targets the original request params; the middleware sets it to the index of the fallback that accepted the request. """ def __init__(self) -> None: self.index = None def __enter__(self) -> BetaFallbackState: token = _fallback_state.set(self) _fallback_state_tokens.set((*_fallback_state_tokens.get(), token)) return self def __exit__(self, *exc_info: object) -> None: tokens = _fallback_state_tokens.get() _fallback_state_tokens.set(tokens[:-1]) _fallback_state.reset(tokens[-1]) _fallback_state: ContextVar[BetaFallbackState | None] = ContextVar("anthropic_beta_fallback_state", default=None) # The reset tokens for every `with state:` block the current context is inside, # innermost last. Kept in a ContextVar — NOT on the state instance — so that one # state shared across threads/tasks (the documented usage) has each context # entering and exiting with its own tokens; a `Token` can only be reset in the # context that created it. _fallback_state_tokens: ContextVar[tuple[Token[BetaFallbackState | None], ...]] = ContextVar( "anthropic_beta_fallback_state_tokens", default=() ) class BetaRefusalFallbackMiddleware(Middleware): """Middleware that retries refused beta `/v1/messages` requests down a fallback chain, reproducing the server-side `fallbacks` wire shape client-side. Only `client.beta.messages` requests are handled — refusals minted by the first-party `client.messages` surface carry no `fallback_credit_token`, so those requests pass through untouched. Each `fallbacks` entry is a patch against the ORIGINAL request params: a field set to a value overrides it, a field explicitly `None` unsets it, an absent field keeps the original value; `output_config` patches its subfields the same way one level deep. Hops never compound — every hop patches the original params, never the previous hop's patched request. Non-streaming: when a response comes back with `stop_reason: "refusal"`, the request is retried with each entry of `fallbacks` applied as a patch to the original params — passing along the refusal's `fallback_credit_token` (in the object form, with `mode: "best_effort"`) when it minted one — until a model accepts or the chain is exhausted. A `fallback` seam block per model boundary is prepended to the served message's content — the same block shape the streaming splice emits. The served hop's `usage` is left verbatim (streaming rewrites it to per-hop `usage.iterations`). Streaming: when the stream ends in `stop_reason: "refusal"`, a second request is issued to the fallback model. It carries the refusal's `fallback_credit_token`, plus the refused model's partial output as a trailing assistant prefill when the refusal grants one (`fallback_has_prefill_claim`). The fallback's events are then spliced onto the still-open stream, so the client sees one continuous message in the server-side `fallbacks` wire shape: a `fallback` content block at each model boundary, monotonic block indices, and per-hop `usage.iterations` on the final `message_delta`. A refusal before any output streamed retries even without a credit token, and the serving hop's `message_start` opens the wire carrying the primary's message id. The fallback-credit beta the credit tokens require is sent by default on every request the middleware handles; the `betas` option controls this. In both modes a fallback that itself refuses with a fresh credit token continues down the chain. A streaming fallback whose appended prefill the server rejects (HTTP 400 body mismatch) is retried once without it; a fallback whose request fails outright is skipped — its token was never redeemed, so it carries to the next entry. When every remaining entry fails over HTTP, the suppressed refusal is replayed to the client with `recommended_model` stamped from the final failure (the failed model for capacity errors, `null` otherwise). A refusal surfaced to the client rather than retried is reported through the `anthropic.lib.middleware` logger. To keep later requests on the model that accepted, run them inside a shared `BetaFallbackState` context; requests sharing that state start directly at the pinned fallback. Reuse one state across whatever scope the pin should apply to — typically a conversation. The state is the only pin: `fallback` seam blocks replayed in the request history are stripped from the outgoing request (an assistant turn left empty by the strip is dropped whole), never read back as a pin. ```py client = Anthropic(middleware=[BetaRefusalFallbackMiddleware([{"model": "claude-opus-4-8"}])]) state = BetaFallbackState() with state: message = client.beta.messages.create(**params) ``` """ def __init__( self, fallbacks: Iterable[BetaFallbackParam], *, betas: Iterable[AnthropicBetaParam] | None = None, ) -> None: """ Args: fallbacks: The fallback chain, tried in order. An empty chain disables the middleware. betas: Betas added to the `anthropic-beta` header of every `/v1/messages` request this middleware handles — the original request included, since refusals only carry a `fallback_credit_token` when the beta is enabled. Defaults to `("fallback-credit-2026-07-01",)`; pass `()` to send none. """ self._fallbacks = tuple(fallbacks) self._betas = DEFAULT_BETAS if betas is None else tuple(betas) self._warned_missing_state = False @override def handle(self, request: APIRequest, call_next: CallNext) -> APIResponse[Any]: body = self._applicable_body(request) if body is None: return call_next(request) state = _fallback_state.get() start_index = self._start_index(state) pin = self._make_pin(state) # Send the configured betas on this and every hop request derived from it, # and tag this and every hop with the middleware's helper telemetry. request = _with_middleware_headers(request, self._betas) # The seam blocks this middleware splices into streams are client-side # markers — the server rejects them as unknown tags — so a history that # replays them is rewritten without them. body = _strip_seam_blocks(body) initial_body = body if start_index == -1 else _apply_hop(body, self._fallbacks[start_index]) initial_request = request.copy(body=initial_body) response = call_next(initial_request) if not response.http_response.is_success: return response if request.stream: first_hop = start_index + 1 # Splicing needs at least one entry left to hop to; otherwise the # stream passes through untouched. if first_hop >= len(self._fallbacks): return response return self._splice_fallback_stream( request=initial_request, body=body, initial_model=str(initial_body.get("model") or ""), response=response, call_next=call_next, first_hop=first_hop, pin=pin, ) index = start_index res = response from_model = str(initial_body.get("model") or "") seams: list[dict[str, Any]] = [] while index < len(self._fallbacks) - 1 and res.http_response.is_success: message = res.parse() if not isinstance(message, (Message, BetaMessage)) or message.stop_reason != "refusal": break index += 1 pin(index) token = _credit_token(message) category = _refusal_category(message) res = call_next(request.copy(body=_merged_body(body, self._fallbacks[index], token))) if res.http_response.is_success: to_model = str(self._fallbacks[index]["model"]) seams.append(_seam_block(from_model, to_model, category)) from_model = to_model if seams and res.http_response.is_success: served = res.parse() if isinstance(served, (Message, BetaMessage)) and served.stop_reason != "refusal": # Prepend one `fallback` seam block per model boundary to the # serving hop's content — the same block shape the streaming # splice emits. return _prepend_seam_blocks(res, seams) return res @override async def handle_async(self, request: APIRequest, call_next: AsyncCallNext) -> AsyncAPIResponse[Any]: body = self._applicable_body(request) if body is None: return await call_next(request) state = _fallback_state.get() start_index = self._start_index(state) pin = self._make_pin(state) # Send the configured betas on this and every hop request derived from it, # and tag this and every hop with the middleware's helper telemetry. request = _with_middleware_headers(request, self._betas) # The seam blocks this middleware splices into streams are client-side # markers — the server rejects them as unknown tags — so a history that # replays them is rewritten without them. body = _strip_seam_blocks(body) initial_body = body if start_index == -1 else _apply_hop(body, self._fallbacks[start_index]) initial_request = request.copy(body=initial_body) response = await call_next(initial_request) if not response.http_response.is_success: return response if request.stream: first_hop = start_index + 1 # Splicing needs at least one entry left to hop to; otherwise the # stream passes through untouched. if first_hop >= len(self._fallbacks): return response return self._splice_fallback_stream_async( request=initial_request, body=body, initial_model=str(initial_body.get("model") or ""), response=response, call_next=call_next, first_hop=first_hop, pin=pin, ) index = start_index res = response from_model = str(initial_body.get("model") or "") seams: list[dict[str, Any]] = [] while index < len(self._fallbacks) - 1 and res.http_response.is_success: message = await res.parse() if not isinstance(message, (Message, BetaMessage)) or message.stop_reason != "refusal": break index += 1 pin(index) token = _credit_token(message) category = _refusal_category(message) res = await call_next(request.copy(body=_merged_body(body, self._fallbacks[index], token))) if res.http_response.is_success: to_model = str(self._fallbacks[index]["model"]) seams.append(_seam_block(from_model, to_model, category)) from_model = to_model if seams and res.http_response.is_success: served = await res.parse() if isinstance(served, (Message, BetaMessage)) and served.stop_reason != "refusal": # Prepend one `fallback` seam block per model boundary to the # serving hop's content — the same block shape the streaming # splice emits. return _prepend_seam_blocks_async(res, seams) return res def _applicable_body(self, request: APIRequest) -> dict[str, Any] | None: """The request's JSON body when this middleware applies to it, `None` otherwise.""" body = _as_dict(request.json) url = httpx.URL(request.url) if ( # an empty chain disables this middleware not self._fallbacks # this middleware only applies to the beta messages API # (`client.beta.messages`, marked by the `beta=true` query param) — # only the beta surface mints fallback credit tokens or request.method.lower() != "post" or url.path != _MESSAGES_PATH or url.params.get("beta") != "true" or body is None ): return None if body.get("fallbacks") is not None: raise AnthropicError( "Sending the `fallbacks:` request param is not supported when using the " "`BetaRefusalFallbackMiddleware`. You should either remove the middleware and send `fallbacks:` with the " "`server-side-fallback-2026-07-01` beta header to let the API handle refusal fallbacks, or omit the " "`fallbacks:` param if you'd like `BetaRefusalFallbackMiddleware` to handle " "fallbacks on the client side." ) return body def _start_index(self, state: BetaFallbackState | None) -> int: """The chain entry this request starts at (-1 = the original params). Only an explicit `BetaFallbackState` pin moves the start; without one the request starts at the original params. """ if state is None or state.index is None: return -1 start_index = state.index if not -1 <= start_index < len(self._fallbacks): raise AnthropicError( f"BetaFallbackState.index {start_index} is out of bounds for a chain of " f"{len(self._fallbacks)} fallback(s); was the state shared with a different middleware?" ) return start_index def _make_pin(self, state: BetaFallbackState | None) -> Callable[[int], None]: """Pin requests sharing the state to the entry being tried (or warn that there is none).""" def pin(index: int) -> None: if state is not None: state.index = index elif not self._warned_missing_state: self._warned_missing_state = True log.warning( "anthropic-sdk: BetaRefusalFallbackMiddleware fell back without an active " "BetaFallbackState; follow-up requests will retry models that already refused. " "Run them inside a shared `with BetaFallbackState():` block to pin them to the " "accepted model." ) return pin def _splice_fallback_stream( self, *, request: APIRequest, body: dict[str, Any], initial_model: str, response: APIResponse[Any], call_next: CallNext, first_hop: int, pin: Callable[[int], None], ) -> APIResponse[Any]: """Wrap the refusable stream in a response whose body passes events through until a retryable refusal, then splices the fallback chain's events on. `body` is the original request params — every hop patches it, never the previous hop's patched body; `initial_model` is the model the initial request actually queried (the pinned entry's when a state pin applied). Closing the returned response (or the `Stream` parsed from it) tears down whichever stream is being read and abandons any in-flight fallback request. """ frames = self._spliced_frames( request=request, body=body, initial_model=initial_model, response=response, call_next=call_next, first_hop=first_hop, pin=pin, ) return APIResponse( raw=_spliced_http_response(response.http_response, _FrameByteStream(frames)), cast_to=response._cast_to, client=response._client, stream=True, stream_cls=response._stream_cls, options=response._options, retries_taken=response.retries_taken, ) def _splice_fallback_stream_async( self, *, request: APIRequest, body: dict[str, Any], initial_model: str, response: AsyncAPIResponse[Any], call_next: AsyncCallNext, first_hop: int, pin: Callable[[int], None], ) -> AsyncAPIResponse[Any]: frames = self._spliced_frames_async( request=request, body=body, initial_model=initial_model, response=response, call_next=call_next, first_hop=first_hop, pin=pin, ) return AsyncAPIResponse( raw=_spliced_http_response(response.http_response, _AsyncFrameByteStream(frames)), cast_to=response._cast_to, client=response._client, stream=True, stream_cls=response._stream_cls, options=response._options, retries_taken=response.retries_taken, ) # --- streaming fallback (credit-token continuation) ------------------------- # # The retry uses the appended-assistant form documented on # `fallback_credit_token`: the refused request's body, extended by one # trailing assistant turn carrying the refused model's partial output. The # token authorizes that turn as a prefill continuation and applies the # fallback credit. The refusal's `fallback_has_prefill_claim` says whether # the partial output may be resent: when true the accumulated blocks are # appended (trailing thinking blocks stripped — an assistant turn cannot # end in one); when false the refused hop's output is dropped and the # token is redeemed against the same body. # # Wire-shape rules (pinned by the fable-fallback conformance suites): # # * A refusal that arrives MID-STREAM keeps the primary's `message_start` # on the wire; the seam block's `to.model` carries the serving model. # A refusal BEFORE any output (pre-stream) holds the wire instead: the # serving hop's `message_start` opens it, with its `id` rewritten to the # primary's, followed by one queued seam per hop that was reached. # * The seam's `from.model` echoes the model string the caller sent (alias # or canonical) while the declining hop is the requested model; fallback # hops use their entry's model id. # * A hop's seam is emitted only once its response arrives OK — a hop whose # request fails over HTTP was never reached and leaves no seam and no # iterations entry; its token and continuation carry to the next entry. # * Refusal text streamed before the refusal stays in the message and is # resent as-is — the appended turn must match the partial output verbatim. def _spliced_frames( self, *, request: APIRequest, body: dict[str, Any], initial_model: str, response: APIResponse[Any], call_next: CallNext, first_hop: int, pin: Callable[[int], None], ) -> Generator[bytes, None, None]: fallbacks = self._fallbacks # the response whose body is currently being consumed; closed on teardown current: httpx.Response | None = response.http_response try: # --- stream A: pass through until a chainable refusal --- stream_a = response.http_response reader = _HopReader( index_base=0, # the caller guarantees first_hop < len(fallbacks) has_next=True, splice=None, wire_open=False, primary_id=None, seam_frames=[], ) outcome = yield from _drive_hop(stream_a, reader) if outcome.refused is None: return # non-refusal or not-retryable: pure pass-through. stream_a.close() current = None # --- fallback chain: try each entry in order --- chain = _ChainState.begin(body, initial_model, outcome) for hop in range(first_hop, len(fallbacks)): entry = fallbacks[hop] model = str(entry["model"]) has_next = hop + 1 < len(fallbacks) pin(hop) # --- build the request: appended-assistant continuation --- # First attempt carries the newest partial appended (when its # refusal granted a prefill claim); a 400 on that form is taken # as the server rejecting the prefill, so the hop is retried # once without it — the same-body form the token always # supports. continuation = chain.continuation() res_b: APIResponse[Any] | None = None failure: _HopFailure | None = None for attempt in range(2): hop_request = request.copy(body=chain.hop_body(entry, continuation)) try: res_b = call_next(hop_request) except Exception as err: log.error( "anthropic-sdk: BetaRefusalFallbackMiddleware: fallback request to %s failed: %s", model, err, ) failure = _HopFailure(model=model, status=None) break if res_b.http_response.is_success: current = res_b.http_response break err_body = _read_json(res_b.http_response) res_b.http_response.close() if attempt == 0 and res_b.status_code == 400 and continuation: log.warning( "anthropic-sdk: BetaRefusalFallbackMiddleware: fallback request with the " "partial output appended was rejected (HTTP 400: %s); retrying without it", _json_dumps(err_body), ) continuation = chain.base res_b = None continue log.error( "anthropic-sdk: BetaRefusalFallbackMiddleware: fallback request to %s failed: HTTP %s: %s", model, res_b.status_code, _json_dumps(err_body), ) failure = _HopFailure(model=model, status=res_b.status_code) break if failure is not None: # The token was never redeemed — retry it against the next entry. if has_next: continue # Every remaining entry failed: degrade to the suppressed # refusal, stamped with the final failure's recommendation. for frame in chain.terminal_failure_frames(failure): yield frame return # --- splice: queued seam, monotonic indices, usage.iterations --- assert res_b is not None hop_response = res_b.http_response chain.queue_seam(model) reader = _HopReader( index_base=chain.next_index, has_next=has_next, splice=_SpliceInfo(iterations=chain.iterations, model=model), wire_open=chain.wire_open, primary_id=chain.primary_id, seam_frames=chain.pending_seam_frames(), ) outcome = yield from _drive_hop(hop_response, reader) if outcome.opened: chain.mark_opened() if outcome.refused is None: return hop_response.close() current = None # This hop refused too: its emitted partial (if any) stays in # the client's message, becomes the next partial segment, and # the chain continues. chain.absorb_refusal(outcome, model, continuation) finally: if current is not None: current.close() async def _spliced_frames_async( self, *, request: APIRequest, body: dict[str, Any], initial_model: str, response: AsyncAPIResponse[Any], call_next: AsyncCallNext, first_hop: int, pin: Callable[[int], None], ) -> AsyncGenerator[bytes, None]: fallbacks = self._fallbacks # the response whose body is currently being consumed; closed on teardown current: httpx.Response | None = response.http_response try: # --- stream A: pass through until a chainable refusal --- stream_a = response.http_response reader = _HopReader( index_base=0, # the caller guarantees first_hop < len(fallbacks) has_next=True, splice=None, wire_open=False, primary_id=None, seam_frames=[], ) async for frame in _drive_hop_async(stream_a, reader): yield frame outcome = reader.finish() if outcome.refused is None: return # non-refusal or not-retryable: pure pass-through. await stream_a.aclose() current = None chain = _ChainState.begin(body, initial_model, outcome) for hop in range(first_hop, len(fallbacks)): entry = fallbacks[hop] model = str(entry["model"]) has_next = hop + 1 < len(fallbacks) pin(hop) continuation = chain.continuation() res_b: AsyncAPIResponse[Any] | None = None failure: _HopFailure | None = None for attempt in range(2): hop_request = request.copy(body=chain.hop_body(entry, continuation)) try: res_b = await call_next(hop_request) except Exception as err: log.error( "anthropic-sdk: BetaRefusalFallbackMiddleware: fallback request to %s failed: %s", model, err, ) failure = _HopFailure(model=model, status=None) break if res_b.http_response.is_success: current = res_b.http_response break err_body = await _read_json_async(res_b.http_response) await res_b.http_response.aclose() if attempt == 0 and res_b.status_code == 400 and continuation: log.warning( "anthropic-sdk: BetaRefusalFallbackMiddleware: fallback request with the " "partial output appended was rejected (HTTP 400: %s); retrying without it", _json_dumps(err_body), ) continuation = chain.base res_b = None continue log.error( "anthropic-sdk: BetaRefusalFallbackMiddleware: fallback request to %s failed: HTTP %s: %s", model, res_b.status_code, _json_dumps(err_body), ) failure = _HopFailure(model=model, status=res_b.status_code) break if failure is not None: # The token was never redeemed — retry it against the next entry. if has_next: continue for frame in chain.terminal_failure_frames(failure): yield frame return assert res_b is not None hop_response = res_b.http_response chain.queue_seam(model) reader = _HopReader( index_base=chain.next_index, has_next=has_next, splice=_SpliceInfo(iterations=chain.iterations, model=model), wire_open=chain.wire_open, primary_id=chain.primary_id, seam_frames=chain.pending_seam_frames(), ) async for frame in _drive_hop_async(hop_response, reader): yield frame outcome = reader.finish() if outcome.opened: chain.mark_opened() if outcome.refused is None: return await hop_response.aclose() current = None chain.absorb_refusal(outcome, model, continuation) finally: if current is not None: await current.aclose() # --- hop consumption --------------------------------------------------------- class _Refusal(BaseModel): token: Optional[str] """The minted credit token; `None` for a token-less start-of-stream refusal.""" category: Optional[str] """The policy category that triggered the refusal; `None` when not surfaced.""" has_prefill_claim: bool usage: Dict[str, Any] event: Dict[str, Any] """The suppressed refusal `message_delta` event, verbatim — replayed if every remaining entry fails over HTTP.""" class _SpliceInfo(BaseModel): """Splice context for fallback hops; `None` for stream A.""" iterations: List[Dict[str, Any]] model: str class _HopFailure(BaseModel): """A fallback hop whose request failed; stamps `recommended_model` when it ends the chain.""" model: str status: Optional[int] """The HTTP status, or `None` when the request raised instead of resolving.""" class _HopOutcome(BaseModel): """The outcome of consuming one hop's stream.""" refused: Optional[_Refusal] """Set when the hop refused and an entry remained to chain to.""" model: Optional[str] """The hop's serving model, from its message_start.""" start_event: Optional[Dict[str, Any]] """The hop's message_start event, parsed.""" blocks: List[Dict[str, Any]] """The hop's accumulated content blocks, in start order — the next partial segment.""" next_index: int """One past the highest (shifted) block index emitted — where the next boundary goes.""" opened: bool """Whether this hop emitted output (its frames opened or continued the wire).""" last_fallback_to: Optional[str] """`to.model` of the last `fallback` seam block seen in this hop's stream — a server-stitched envelope's last decliner.""" class _HopReader: """Consumes one hop's SSE events, producing the frames to forward to the client while accumulating its content blocks. Events are held until the hop produces output (a content block or a terminal event), so a refusal that arrives before any output — a pre-stream decline — leaves no trace on the wire and chains silently. On open, stream A's held `message_start` is flushed in its original wire bytes; a spliced hop's is suppressed when the wire is already open, or emitted with its `id` rewritten to the primary's when this hop's start opens the wire. Queued seam frames for every hop reached so far are flushed right after. A spliced hop has its block indices shifted by `index_base` and its terminal message_delta's usage rewritten to the `usage.iterations` chain shape. A refusal that can be chained — an entry remains, and either a `fallback_credit_token` was minted or nothing has streamed yet — ends the hop early: open blocks are closed, the terminal message_delta + message_stop are suppressed, and the refusal is set on the outcome so the caller can issue the next hop. Any other refusal is logged and passes through to the client. """ outcome: _HopOutcome | None """Set when a chainable refusal ended the hop early; the driver stops feeding.""" def __init__( self, *, index_base: int, has_next: bool, splice: _SpliceInfo | None, wire_open: bool, primary_id: str | None, seam_frames: list[bytes], ) -> None: self._tracker = _BlockTracker(index_base) self._has_next = has_next self._splice = splice self._wire_open = wire_open self._primary_id = primary_id self._seam_frames = seam_frames self._model: str | None = None self._start_usage: dict[str, Any] | None = None self._start_event: dict[str, Any] | None = None self._held_start: ServerSentEvent | None = None self._held: list[ServerSentEvent] = [] self._opened = False self._last_fallback_to: str | None = None self.outcome = None @property def opened(self) -> bool: return self._opened def feed(self, sse: ServerSentEvent) -> list[bytes]: """Process one event, returning the frames to forward for it.""" event = _as_dict(_safe_json(sse.data)) event_type = event.get("type") if event is not None else None splice = self._splice if event_type == "message_start" and event is not None: message = _as_dict(event.get("message")) if message is not None: model = message.get("model") self._model = model if isinstance(model, str) else None self._start_usage = _as_dict(message.get("usage")) self._start_event = event self._held_start = sse return [] elif event_type == "content_block_start" and event is not None: frames = self._open() content_block = _as_dict(event.get("content_block")) if content_block is not None and content_block.get("type") == "fallback": to = _as_dict(content_block.get("to")) to_model = to.get("model") if to is not None else None if isinstance(to_model, str): self._last_fallback_to = to_model self._tracker.start(event) return ( [*frames, _emit("content_block_start", event)] if splice is not None else [*frames, _passthrough_sse(sse)] ) elif event_type == "content_block_delta" and event is not None: frames = self._open() self._tracker.delta(event) return ( [*frames, _emit("content_block_delta", event)] if splice is not None else [*frames, _passthrough_sse(sse)] ) elif event_type == "content_block_stop" and event is not None: frames = self._open() self._tracker.stop(event) return ( [*frames, _emit("content_block_stop", event)] if splice is not None else [*frames, _passthrough_sse(sse)] ) elif event_type == "message_delta" and event is not None: delta = _as_dict(event.get("delta")) or {} if delta.get("stop_reason") == "refusal": stop_details = _as_dict(delta.get("stop_details")) details = stop_details if stop_details is not None and stop_details.get("type") == "refusal" else None token = details.get("fallback_credit_token") if details is not None else None token = token if isinstance(token, str) and token else None pre_stream = not self._opened and not self._tracker.content_blocks() # A mid-stream refusal chains only when it minted a credit token; # a pre-stream refusal chains even without one — nothing has # streamed, so the retry is free and invisible. if self._has_next and (token is not None or pre_stream): usage = _backfill(_as_dict(event.get("usage")), self._start_usage) frames = list(self._tracker.close_open_blocks()) # suppress this hop's message_delta + message_stop (and, for a # pre-stream decline, everything held before them) self._held_start = None self._held.clear() self.outcome = _HopOutcome( refused=_Refusal( token=token, category=details.get("category") if details is not None else None, has_prefill_claim=details is not None and details.get("fallback_has_prefill_claim") is True, usage=usage, event=event, ), model=self._model, start_event=self._start_event, blocks=self._tracker.content_blocks(), next_index=self._tracker.next_index, opened=self._opened, last_fallback_to=self._last_fallback_to, ) return frames if not token: log.error( "anthropic-sdk: BetaRefusalFallbackMiddleware: refusal stop_details has no " "fallback_credit_token; surfacing the refusal" ) else: log.error( "anthropic-sdk: BetaRefusalFallbackMiddleware: refusal but no fallback " "entries remain; surfacing the refusal" ) frames = self._open() if splice is not None: if delta.get("stop_reason") == "refusal": # the terminal refusal's stop_details always carries a model # recommendation slot in the stitched shape stop_details = _as_dict(delta.get("stop_details")) if stop_details is not None: stop_details.setdefault("recommended_model", None) # Terminal hop. Replace iterations, don't append: this hop's own # message_delta self-reports its iterations without a `model` (a # fresh non-fallback request doesn't know it served a chain). # Server-side `fallbacks` relabels the whole chain instead — # refused hops as `message`, the serving hop as # `fallback_message` — so the recorded chain replaces the # self-report, with this hop's own entry relabeled as the # `fallback_message` completer. usage = _as_dict(event.get("usage")) or {} usage["iterations"] = [ *splice.iterations, _serving_iteration_entry(usage, splice.model), ] event["usage"] = usage return [*frames, _emit("message_delta", event)] return [*frames, _passthrough_sse(sse)] if not self._opened: # ping, error, unrecognised — held until the wire-open decision self._held.append(sse) return [] # message_stop, ping, error, unrecognised — and for stream A every # event — pass through in their original wire bytes. return [_passthrough_sse(sse)] def _open(self) -> list[bytes]: """Open the wire for this hop: its message_start (raw for stream A; suppressed or id-rewritten for a spliced hop), the queued seam frames, then anything else held.""" if self._opened: return [] self._opened = True frames: list[bytes] = [] if self._splice is None: if self._held_start is not None: frames.append(_passthrough_sse(self._held_start)) elif not self._wire_open and self._start_event is not None: start_event = _copy.deepcopy(self._start_event) message = _as_dict(start_event.get("message")) if message is not None and self._primary_id is not None: message["id"] = self._primary_id frames.append(_emit("message_start", start_event)) frames.extend(self._seam_frames) frames.extend(_passthrough_sse(held) for held in self._held) self._held_start = None self._held.clear() return frames def finish_frames(self) -> list[bytes]: """Frames still owed when the stream ends without a chainable refusal.""" if self.outcome is not None or self._opened: return [] return self._open() def finish(self) -> _HopOutcome: """The outcome once the hop's stream is fully consumed (or cut early).""" if self.outcome is not None: return self.outcome return _HopOutcome( refused=None, model=self._model, start_event=self._start_event, blocks=self._tracker.content_blocks(), next_index=self._tracker.next_index, opened=self._opened, last_fallback_to=self._last_fallback_to, ) def _drive_hop(response: httpx.Response, reader: _HopReader) -> Generator[bytes, None, _HopOutcome]: """Feed one hop's SSE events through `reader`, yielding the frames to forward.""" for sse in Stream.raw_events(response): for frame in reader.feed(sse): yield frame if reader.outcome is not None: break for frame in reader.finish_frames(): yield frame return reader.finish() async def _drive_hop_async(response: httpx.Response, reader: _HopReader) -> AsyncIterator[bytes]: """Feed one hop's SSE events through `reader`, yielding the frames to forward. The outcome is read from `reader.finish()` afterwards — async generators cannot return a value. """ async for sse in AsyncStream.raw_events(response): for frame in reader.feed(sse): yield frame if reader.outcome is not None: break for frame in reader.finish_frames(): yield frame class _ChainState: """Bookkeeping shared across the hops of one spliced stream.""" next_index: int """Monotonic block index across all spliced streams.""" wire_open: bool """Whether a message_start has reached the client yet.""" primary_id: str | None """The primary's message id — stamped onto the message_start that opens the wire when the primary declined pre-stream.""" primary_model: str """The model string the initial request queried (alias or canonical; the pinned entry's when a state pin applied) — echoed by the first seam's `from.model` and the first iterations entry.""" token: str | None base: List[Any] partial: List[Any] from_model: str last_refusal: _Refusal last_start_event: Dict[str, Any] | None iterations: List[Dict[str, Any]] """One `message` entry per hop that was reached and declined, in order — the primary first. Failed hops are skipped (no usage came back); the serving hop is appended as `fallback_message` when its message_delta arrives.""" def __init__(self, body: dict[str, Any]) -> None: self._body = body self._pending_seams: list[bytes] = [] @classmethod def begin(cls, body: dict[str, Any], initial_model: str, outcome: _HopOutcome) -> _ChainState: """The chain state after stream A's chainable refusal. `body` is the original request params (pre-pin) — the base every hop patches; `initial_model` is the model the initial request queried. """ assert outcome.refused is not None chain = cls(body) chain.primary_model = initial_model chain.next_index = outcome.next_index chain.wire_open = outcome.opened start_message = _as_dict((outcome.start_event or {}).get("message")) start_id = start_message.get("id") if start_message is not None else None chain.primary_id = start_id if isinstance(start_id, str) else None chain.token = outcome.refused.token chain.base = [] chain.partial = _to_prefill_blocks(outcome.blocks) if outcome.refused.has_prefill_claim else [] # A server-stitched envelope's last decliner is the model the first # client-side seam hands off from; a plain stream hands off from the # caller-spelled primary. chain.from_model = outcome.last_fallback_to or chain.primary_model chain.last_refusal = outcome.refused chain.last_start_event = outcome.start_event chain.iterations = _declined_iteration_entries(outcome.refused, chain.primary_model) return chain def queue_seam(self, to_model: str) -> None: """Queue the reached hop's `fallback` seam block at the next monotonic index. Queued seams are flushed by the hop reader once output reaches the wire, so a chain of pre-stream declines lines its seams up after the serving hop's message_start. """ seam_index = self.next_index self.next_index += 1 self._pending_seams.extend( [ _emit( "content_block_start", { "type": "content_block_start", "index": seam_index, "content_block": _seam_block(self.from_model, to_model, self.last_refusal.category), }, ), _emit("content_block_stop", {"type": "content_block_stop", "index": seam_index}), ] ) self.from_model = to_model def pending_seam_frames(self) -> list[bytes]: return list(self._pending_seams) def mark_opened(self) -> None: self.wire_open = True self._pending_seams.clear() def continuation(self) -> list[Any]: return [*self.base, *self.partial] def hop_body(self, entry: BetaFallbackParam, continuation: list[Any]) -> dict[str, Any]: """The hop's request body: the entry applied as a patch against the ORIGINAL request params (never a previous hop's patched body), extended by the continuation. The server-side `fallbacks` param is stripped — it is mutually exclusive with a credit-token retry. When the refusal granted no prefill claim the appended turn is omitted entirely and the same-body form is sent. """ body: dict[str, Any] = { key: value for key, value in _apply_hop(self._body, entry).items() if key != "fallbacks" } if self.token is not None: body["fallback_credit_token"] = _credit_token_param(self.token) if continuation: messages = body.get("messages") body["messages"] = [ *(messages if isinstance(messages, list) else []), {"role": "assistant", "content": continuation}, ] return body def absorb_refusal(self, outcome: _HopOutcome, model: str, continuation: list[Any]) -> None: """Fold a refused hop's outcome in, readying the next hop.""" assert outcome.refused is not None self.token = outcome.refused.token self.base = continuation self.partial = _to_prefill_blocks(outcome.blocks) if outcome.refused.has_prefill_claim else [] self.iterations.extend(_declined_iteration_entries(outcome.refused, model)) self.last_refusal = outcome.refused if outcome.start_event is not None: self.last_start_event = outcome.start_event self.next_index = outcome.next_index def terminal_failure_frames(self, failure: _HopFailure) -> list[bytes]: """Degrade to the suppressed refusal when every remaining entry failed over HTTP: replay its message_delta verbatim — `recommended_model` stamped from the final failure (the failed model for capacity errors, `null` otherwise) and `usage.iterations` carrying the recorded chain — then message_stop. """ frames: list[bytes] = [] if not self.wire_open and self.last_start_event is not None: start_event = _copy.deepcopy(self.last_start_event) message = _as_dict(start_event.get("message")) if message is not None and self.primary_id is not None: message["id"] = self.primary_id frames.append(_emit("message_start", start_event)) frames.extend(self._pending_seams) event = _copy.deepcopy(self.last_refusal.event) delta = _as_dict(event.get("delta")) if delta is not None: details = _as_dict(delta.get("stop_details")) if details is not None: details["recommended_model"] = failure.model if failure.status in (429, 529) else None usage = _as_dict(event.get("usage")) or {} usage["iterations"] = _copy.deepcopy(self.iterations) event["usage"] = usage frames.append(_emit("message_delta", event)) frames.append(_emit("message_stop", {"type": "message_stop"})) return frames def _declined_iteration_entries(refusal: _Refusal, model_label: str) -> list[dict[str, Any]]: """The `usage.iterations` entries a declined hop contributes to the chain. The hop's refusal self-reports its iterations: adopt them, retyped to `message` (every one of them declined — a server-stitched envelope labels its last hop `fallback_message`). A single-entry self-report describes the hop itself and gains its model — `model_label`, the caller-spelled primary or the chain entry's id — when the wire didn't name one; a multi-entry self-report (a server-tool loop) is kept verbatim. Without a self-report, one entry is built from the refusal's usage. """ reported = refusal.usage.get("iterations") if isinstance(reported, list) and reported: dict_entries = (_as_dict(entry) for entry in cast("List[Any]", reported)) entries: list[dict[str, Any]] = [{**entry, "type": "message"} for entry in dict_entries if entry is not None] if entries: if len(entries) == 1 and not isinstance(entries[0].get("model"), str): entries[0]["model"] = model_label return entries return [_to_iteration_usage("message", model_label, refusal.usage)] def _serving_iteration_entry(delta_usage: dict[str, Any], model: str) -> dict[str, Any]: """The serving hop's `fallback_message` completer entry: its own self-reported iteration relabeled, or one built from its delta usage.""" reported = delta_usage.get("iterations") if isinstance(reported, list) and reported: last = _as_dict(cast("List[Any]", reported)[-1]) if last is not None: return {**last, "type": "fallback_message", "model": last.get("model") or model} return _to_iteration_usage("fallback_message", model, delta_usage) class _BlockTracker: """Block bookkeeping for one stream of the splice: accumulates each content block from its deltas (for the continuation prefill), shifts wire indices by `index_base` so they stay monotonic across hops, and tracks which blocks are still open so a refusal that cuts mid-block can close them. """ next_index: int """One past the highest shifted block index seen.""" def __init__(self, index_base: int = 0) -> None: self._index_base = index_base self.next_index = index_base # the stream's accumulated blocks keyed by their original wire index self._blocks: list[tuple[int, dict[str, Any]]] = [] # shifted indices of blocks started but not yet stopped self._open: list[int] = [] def content_blocks(self) -> list[dict[str, Any]]: """The accumulated content blocks, in start order.""" return [block for _, block in self._blocks] def start(self, event: dict[str, Any]) -> None: """Track a content_block_start, shifting `event["index"]`.""" index = event.get("index") if not isinstance(index, int): return content_block = _as_dict(event.get("content_block")) self._blocks.append((index, dict(content_block) if content_block is not None else {})) shifted = index + self._index_base event["index"] = shifted self._open.append(shifted) self.next_index = max(self.next_index, shifted + 1) def delta(self, event: dict[str, Any]) -> None: """Apply a content_block_delta to its accumulating block, shifting `event["index"]`.""" index = event.get("index") if not isinstance(index, int): return delta = _as_dict(event.get("delta")) if delta is not None: _apply_delta(self._blocks, index, delta) event["index"] = index + self._index_base def stop(self, event: dict[str, Any]) -> None: """Track a content_block_stop, shifting `event["index"]`.""" index = event.get("index") if not isinstance(index, int): return shifted = index + self._index_base event["index"] = shifted if shifted in self._open: self._open.remove(shifted) self.next_index = max(self.next_index, shifted + 1) def close_open_blocks(self) -> Iterator[bytes]: """content_block_stop events for any blocks still open.""" for index in self._open: yield _emit("content_block_stop", {"type": "content_block_stop", "index": index}) self._open.clear() # --- block accumulation & prefill conversion ------------------------------- def _apply_delta(blocks: list[tuple[int, dict[str, Any]]], index: int, delta: dict[str, Any]) -> None: """Apply a content_block_delta to the accumulating block at `index`.""" block = next((block for block_index, block in blocks if block_index == index), None) if block is None: return delta_type = delta.get("type") if delta_type == "text_delta": block["text"] = str(block.get("text") or "") + str(delta.get("text") or "") elif delta_type == "input_json_delta": block["_partial_json"] = str(block.get("_partial_json") or "") + str(delta.get("partial_json") or "") elif delta_type == "citations_delta": citations = block.get("citations") if not isinstance(citations, list): citations = [] block["citations"] = citations cast("List[Any]", citations).append(delta.get("citation")) elif delta_type == "thinking_delta": block["thinking"] = str(block.get("thinking") or "") + str(delta.get("thinking") or "") elif delta_type == "signature_delta": block["signature"] = delta.get("signature") def _to_prefill_blocks(response_blocks: list[dict[str, Any]]) -> list[Any]: """Convert a hop's accumulated response blocks to the appended assistant turn. A `fallback_has_prefill_claim` refusal guarantees the partial output is resendable, so the blocks go out near-verbatim. Three rewrites apply: `fallback` seam blocks (wire markers, not content) are dropped, tool inputs are reassembled from their accumulated `input_json_delta` JSON (content_block_start carries `input: {}`), and trailing thinking blocks are stripped — the server rejects an assistant turn whose final block is `thinking`, so a refusal that cut the stream mid-thought would otherwise 400 the continuation. A partial that was nothing but thinking strips to empty, and the hop falls back to the same-body form. """ out: list[Any] = [] for block in response_blocks: if block.get("type") == "fallback": continue partial_json = block.get("_partial_json") if not isinstance(partial_json, str): out.append(block) continue block = {key: value for key, value in block.items() if key != "_partial_json"} parsed = _safe_json(partial_json) block["input"] = parsed if parsed is not None else block.get("input") out.append(block) while out and out[-1].get("type") in ("thinking", "redacted_thinking"): out.pop() return out # --- helpers -------------------------------------------------------------- def _strip_seam_blocks(body: dict[str, Any]) -> dict[str, Any]: """A copy of `body` with `{type: "fallback"}` seam blocks filtered out of the replayed message history; `body` itself when there are none. An assistant turn left with no content by the strip — a seam-only turn — is dropped whole; the server rejects empty-content turns.""" messages = body.get("messages") if not isinstance(messages, list): return body stripped = False stripped_messages: list[Any] = [] for message in cast("List[Any]", messages): message_dict = _as_dict(message) content = message_dict.get("content") if message_dict is not None else None if message_dict is None or not isinstance(content, list): stripped_messages.append(message) continue content_list = cast("List[Any]", content) kept = [block for block in content_list if not (is_dict(block) and block.get("type") == "fallback")] if len(kept) == len(content_list): stripped_messages.append(message) continue stripped = True if not kept and message_dict.get("role") == "assistant": continue stripped_messages.append({**message_dict, "content": kept}) if not stripped: return body return {**body, "messages": stripped_messages} def _with_middleware_headers(request: APIRequest, betas: tuple[AnthropicBetaParam, ...]) -> APIRequest: """A copy of `request` with `betas` appended to its `anthropic-beta` header (skipping values already present) and the middleware's helper-telemetry tag appended to `x-stainless-helper`. Single `request.copy()` for both. """ # httpx.Headers for the case-insensitive read; Omit-valued entries can't be # the beta header's value and are carried over untouched below. current = httpx.Headers({k: v for k, v in request.headers.items() if isinstance(v, str)}).get("anthropic-beta", "") existing = {value.strip() for value in current.split(",")} additions = dict.fromkeys(str(beta) for beta in betas if str(beta) not in existing) headers = {k: v for k, v in request.headers.items() if k.lower() != "anthropic-beta"} if current or additions: headers["anthropic-beta"] = ", ".join(filter(None, [current, *additions])) return request.copy(headers=merge_headers(headers, helper_header("fallback-refusal-middleware"))) def _credit_token(message: Message | BetaMessage) -> str | None: """The refusal's minted credit token; only the beta surface carries one.""" if isinstance(message, BetaMessage) and message.stop_details is not None: return message.stop_details.fallback_credit_token return None def _refusal_category(message: Message | BetaMessage) -> str | None: """The policy category that caused the refusal; only the beta surface carries one.""" if isinstance(message, BetaMessage) and message.stop_details is not None: return message.stop_details.category return None def _seam_block(from_model: str, to_model: str, category: str | None) -> dict[str, Any]: """The synthetic `fallback` content block marking one model boundary.""" return { "type": "fallback", "from": {"model": from_model}, "to": {"model": to_model}, "trigger": {"type": "refusal", "category": category}, } def _seamed_http_response(original: httpx.Response, seams: list[dict[str, Any]]) -> httpx.Response | None: """A copy of the served hop's response with `seams` prepended to its `content`, or `None` when the body isn't the expected message shape. The caller has already parsed the response, so its body is buffered. """ message = _as_dict(_safe_json(original.content.decode("utf-8"))) if message is None or not isinstance(message.get("content"), list): return None body = _json_dumps({**message, "content": [*seams, *cast("List[Any]", message["content"])]}).encode("utf-8") headers = original.headers.copy() for header in ("content-encoding", "content-length"): if header in headers: del headers[header] return httpx.Response( status_code=original.status_code, headers=headers, content=body, request=original.request, ) def _prepend_seam_blocks(response: APIResponse[Any], seams: list[dict[str, Any]]) -> APIResponse[Any]: raw = _seamed_http_response(response.http_response, seams) if raw is None: return response return APIResponse( raw=raw, cast_to=response._cast_to, client=response._client, stream=False, stream_cls=response._stream_cls, options=response._options, retries_taken=response.retries_taken, ) def _prepend_seam_blocks_async(response: AsyncAPIResponse[Any], seams: list[dict[str, Any]]) -> AsyncAPIResponse[Any]: raw = _seamed_http_response(response.http_response, seams) if raw is None: return response return AsyncAPIResponse( raw=raw, cast_to=response._cast_to, client=response._client, stream=False, stream_cls=response._stream_cls, options=response._options, retries_taken=response.retries_taken, ) def _apply_hop(body: dict[str, Any], entry: BetaFallbackParam) -> dict[str, Any]: """`entry` applied as a patch against `body`: a field set to a value overrides it, a field explicitly `None` unsets it (absent from the retried request — not sent as `null`), an absent field keeps the original value. `output_config` patches one level deep: its subfields follow the same set / `None`-unsets / absent-keeps rule against the original request's `output_config`; the whole object is dropped when nothing is left. Always a fresh dict; `body` is never mutated.""" patched = _patch(body, cast("Dict[str, Any]", entry)) output_config = _as_dict(entry.get("output_config")) if output_config is not None: merged = _patch(_as_dict(body.get("output_config")) or {}, output_config) if merged: patched["output_config"] = merged else: patched.pop("output_config", None) return patched def _patch(base: dict[str, Any], overrides: dict[str, Any]) -> dict[str, Any]: """`overrides` applied flat against `base`: a key set to a value overrides it, a key explicitly `None` unsets it, an absent key keeps the base value. Always a fresh dict.""" patched = dict(base) for key, value in overrides.items(): if value is None: patched.pop(key, None) else: patched[key] = value return patched def _merged_body(body: dict[str, Any], fallback: BetaFallbackParam, credit_token: str | None) -> dict[str, Any]: """The non-streaming retry body: the fallback entry applied as a patch against the original params, plus the refusal's credit token when it minted one.""" merged = _apply_hop(body, fallback) if credit_token: merged["fallback_credit_token"] = _credit_token_param(credit_token) return merged def _safe_json(text: str) -> Any: try: return json.loads(text) except Exception: return None def _json_dumps(value: Any) -> str: return json.dumps(value, separators=(",", ":")) def _as_dict(value: object) -> dict[str, Any] | None: return cast("Dict[str, Any]", value) if is_dict(value) else None def _read_json(response: httpx.Response) -> Any: try: return json.loads(response.read()) except Exception: return None async def _read_json_async(response: httpx.Response) -> Any: try: return json.loads(await response.aread()) except Exception: return None def _emit(event: str, payload: dict[str, Any]) -> bytes: return _serialize_sse(event=event, data=_json_dumps(payload)).encode("utf-8") def _passthrough_sse(sse: ServerSentEvent) -> bytes: """Forward a decoded event in its original wire bytes, preserving SSE fields beyond `event:`/`data:` (`id:`, `retry:`, comment lines). Falls back to re-serializing for events with no raw lines. """ if sse.raw: return ("\n".join(sse.raw) + "\n\n").encode("utf-8") return _serialize_sse(event=sse.event, data=sse.data).encode("utf-8") def _serialize_sse(*, event: str | None, data: str) -> str: """Serialize an event back to its SSE wire form (`event: ...\\ndata: ...\\n\\n`). Multi-line `data` is emitted as one `data:` line per line, matching the spec. The inverse of the decoder behind `Stream.raw_events`. """ out = "" if event is not None: out += f"event: {event}\n" for line in data.split("\n"): out += f"data: {line}\n" return out + "\n" def _to_iteration_usage( type: Literal["message", "fallback_message"], model: str, usage: dict[str, Any] | None ) -> dict[str, Any]: u = usage or {} return { "type": type, "model": model, "input_tokens": u.get("input_tokens") or 0, "output_tokens": u.get("output_tokens") or 0, "cache_read_input_tokens": u.get("cache_read_input_tokens") or 0, "cache_creation_input_tokens": u.get("cache_creation_input_tokens") or 0, "cache_creation": u.get("cache_creation"), } def _backfill(primary: dict[str, Any] | None, fallback: dict[str, Any] | None) -> dict[str, Any]: """Fill `None` fields on `primary` from `fallback`.""" fallback = fallback or {} out: dict[str, Any] = {**fallback, **(primary or {})} for key, value in out.items(): if value is None and fallback.get(key) is not None: out[key] = fallback[key] return out def _spliced_http_response( original: httpx.Response, stream: httpx.SyncByteStream | httpx.AsyncByteStream ) -> httpx.Response: """A synthetic response standing in for `original`, with `stream` as its body. The spliced frames are emitted post-decode, so the original's content-encoding/length headers no longer describe the body. """ headers = original.headers.copy() for header in ("content-encoding", "content-length"): if header in headers: del headers[header] return httpx.Response( status_code=original.status_code, headers=headers, stream=stream, request=original.request, ) class _FrameByteStream(httpx.SyncByteStream): def __init__(self, frames: Generator[bytes, None, None]) -> None: self._frames = frames @override def __iter__(self) -> Iterator[bytes]: return self._frames @override def close(self) -> None: self._frames.close() class _AsyncFrameByteStream(httpx.AsyncByteStream): def __init__(self, frames: AsyncGenerator[bytes, None]) -> None: self._frames = frames @override def __aiter__(self) -> AsyncIterator[bytes]: return self._frames @override async def aclose(self) -> None: await self._frames.aclose() anthropic-sdk-python-0.120.2/src/anthropic/lib/sessions/000077500000000000000000000000001523216435200231365ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/lib/sessions/__init__.py000066400000000000000000000002311523216435200252430ustar00rootroot00000000000000from ._accumulate import AccumulatedEvent, accumulate_managed_agents_event __all__ = [ "AccumulatedEvent", "accumulate_managed_agents_event", ] anthropic-sdk-python-0.120.2/src/anthropic/lib/sessions/_accumulate.py000066400000000000000000000147431523216435200260030ustar00rootroot00000000000000from __future__ import annotations from typing import TYPE_CHECKING, overload from datetime import datetime, timezone from typing_extensions import TypeAlias, assert_never from ..._compat import model_copy from ..._models import build from ..._exceptions import AnthropicError from ...types.beta.sessions import BetaManagedAgentsAgentMessageEvent, BetaManagedAgentsStreamSessionEvents __all__ = ["AccumulatedEvent", "accumulate_managed_agents_event"] AccumulatedEvent: TypeAlias = BetaManagedAgentsAgentMessageEvent # Placeholder `processed_at` (the Unix epoch) for a preview snapshot until the # buffered final event, which carries the real timestamp, replaces it. _UNPROCESSED = datetime(1970, 1, 1, tzinfo=timezone.utc) @overload def accumulate_managed_agents_event( accumulated: AccumulatedEvent | None, event: BetaManagedAgentsAgentMessageEvent, ) -> BetaManagedAgentsAgentMessageEvent: ... @overload def accumulate_managed_agents_event( accumulated: AccumulatedEvent | None, event: BetaManagedAgentsStreamSessionEvents, ) -> AccumulatedEvent | None: ... def accumulate_managed_agents_event( accumulated: AccumulatedEvent | None, event: BetaManagedAgentsStreamSessionEvents, ) -> AccumulatedEvent | None: """Fold one preview event into an ``agent.message`` snapshot. Returns a fresh snapshot — the ``accumulated`` argument is never mutated. - ``event_start`` opens the preview: a new snapshot with empty content is returned (so ``accumulated`` may be ``None``). Its ``processed_at`` is an epoch placeholder that the buffered final event's server timestamp replaces. ``accumulated`` is passed through unchanged when the previewed event is not an ``agent.message`` — this helper only tracks ``agent.message`` previews. - ``event_delta`` is folded into ``accumulated``: a new ``delta.index`` inserts the fragment as a fresh content entry; an existing index returns a copy with that entry appended to. An unrecognised fragment type on an existing index passes the entry through unchanged — deltas are best-effort and the buffered final event is canonical — but is a type-check-time error via the exhaustiveness guard, matching ``accumulate_event`` in ``lib/streaming/_messages.py``. - ``agent.message`` is the buffered final event: a copy of it is returned, replacing whatever the preview had accumulated. """ if event.type == "event_start": if event.event.type == "agent.message": return build( BetaManagedAgentsAgentMessageEvent, id=event.event.id, type="agent.message", content=[], processed_at=_UNPROCESSED, ) elif event.event.type == "agent.thinking": # This helper only tracks agent.message previews; agent.thinking # previews are start-only and have no deltas to fold. return accumulated else: # we only want exhaustive checking for linters, not at runtime if TYPE_CHECKING: # type: ignore[unreachable] assert_never(event.event) return accumulated elif event.type == "agent.message": return model_copy(event, deep=True) elif event.type == "event_delta": if accumulated is None: raise AnthropicError(f"event_delta for {event.event_id} received before its event_start") idx = event.delta.index if idx is None: idx = 0 fragment = event.delta.content # Indices arrive in order — the first delta at a new index opens the slot. # A gap means deltas arrived out of order or were mis-routed. if idx > len(accumulated.content): raise AnthropicError( f"event_delta index {idx} is beyond the end of content (length {len(accumulated.content)})", ) content = list(accumulated.content) if idx == len(content): # New index: pass the fragment through as a fresh block. content.append(model_copy(fragment)) else: existing = content[idx] if fragment.type == "text": if existing.type == "text": updated = model_copy(existing) updated.text = existing.text + fragment.text content[idx] = updated else: # we only want exhaustive checking for linters, not at runtime if TYPE_CHECKING: # type: ignore[unreachable] assert_never(fragment.type) snapshot = model_copy(accumulated) snapshot.content = content return snapshot elif ( event.type == "user.message" or event.type == "user.interrupt" or event.type == "user.tool_confirmation" or event.type == "user.tool_result" or event.type == "user.custom_tool_result" or event.type == "user.define_outcome" or event.type == "agent.thinking" or event.type == "agent.tool_use" or event.type == "agent.tool_result" or event.type == "agent.custom_tool_use" or event.type == "agent.mcp_tool_use" or event.type == "agent.mcp_tool_result" or event.type == "agent.thread_message_received" or event.type == "agent.thread_message_sent" or event.type == "agent.thread_context_compacted" or event.type == "session.error" or event.type == "session.updated" or event.type == "session.deleted" or event.type == "session.status_running" or event.type == "session.status_idle" or event.type == "session.status_rescheduled" or event.type == "session.status_terminated" or event.type == "session.thread_created" or event.type == "session.thread_status_running" or event.type == "session.thread_status_idle" or event.type == "session.thread_status_rescheduled" or event.type == "session.thread_status_terminated" or event.type == "span.model_request_start" or event.type == "span.model_request_end" or event.type == "span.outcome_evaluation_start" or event.type == "span.outcome_evaluation_ongoing" or event.type == "span.outcome_evaluation_end" or event.type == "system.message" ): return accumulated else: # we only want exhaustive checking for linters, not at runtime if TYPE_CHECKING: # type: ignore[unreachable] assert_never(event) return accumulated anthropic-sdk-python-0.120.2/src/anthropic/lib/streaming/000077500000000000000000000000001523216435200232615ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/lib/streaming/__init__.py000066400000000000000000000030021523216435200253650ustar00rootroot00000000000000from typing_extensions import TypeAlias from ._types import ( TextEvent as TextEvent, InputJsonEvent as InputJsonEvent, MessageStopEvent as MessageStopEvent, MessageStreamEvent as MessageStreamEvent, ContentBlockStopEvent as ContentBlockStopEvent, ParsedMessageStopEvent as ParsedMessageStopEvent, ParsedMessageStreamEvent as ParsedMessageStreamEvent, ParsedContentBlockStopEvent as ParsedContentBlockStopEvent, ) from ._messages import ( MessageStream as MessageStream, AsyncMessageStream as AsyncMessageStream, MessageStreamManager as MessageStreamManager, AsyncMessageStreamManager as AsyncMessageStreamManager, ) from ._beta_types import ( BetaInputJsonEvent as BetaInputJsonEvent, ParsedBetaTextEvent as ParsedBetaTextEvent, ParsedBetaMessageStopEvent as ParsedBetaMessageStopEvent, ParsedBetaMessageStreamEvent as ParsedBetaMessageStreamEvent, ParsedBetaContentBlockStopEvent as ParsedBetaContentBlockStopEvent, ) # For backwards compatibility BetaTextEvent: TypeAlias = ParsedBetaTextEvent BetaMessageStopEvent: TypeAlias = ParsedBetaMessageStopEvent[object] BetaMessageStreamEvent: TypeAlias = ParsedBetaMessageStreamEvent BetaContentBlockStopEvent: TypeAlias = ParsedBetaContentBlockStopEvent[object] from ._beta_messages import ( BetaMessageStream as BetaMessageStream, BetaAsyncMessageStream as BetaAsyncMessageStream, BetaMessageStreamManager as BetaMessageStreamManager, BetaAsyncMessageStreamManager as BetaAsyncMessageStreamManager, ) anthropic-sdk-python-0.120.2/src/anthropic/lib/streaming/_beta_messages.py000066400000000000000000000523471523216435200266070ustar00rootroot00000000000000from __future__ import annotations import builtins from types import TracebackType from typing import TYPE_CHECKING, Any, Type, Generic, Callable, cast from typing_extensions import Self, Iterator, Awaitable, AsyncIterator, assert_never import httpx from pydantic import BaseModel from anthropic.types.beta.beta_tool_use_block import BetaToolUseBlock from anthropic.types.beta.beta_mcp_tool_use_block import BetaMCPToolUseBlock from anthropic.types.beta.beta_server_tool_use_block import BetaServerToolUseBlock from ..._types import NOT_GIVEN, NotGiven from ..._utils import consume_sync_iterator, consume_async_iterator from ..._models import build, construct_type, construct_type_unchecked from ._beta_types import ( BetaCitationEvent, BetaThinkingEvent, BetaInputJsonEvent, BetaSignatureEvent, BetaCompactionEvent, ParsedBetaTextEvent, ParsedBetaMessageStopEvent, ParsedBetaMessageStreamEvent, ParsedBetaContentBlockStopEvent, ) from ..._streaming import Stream, AsyncStream from ...types.beta import BetaRawMessageStreamEvent from ..._utils._utils import is_given from .._parse._response import ResponseFormatT, parse_text from ...types.beta.parsed_beta_message import ParsedBetaMessage, ParsedBetaContentBlock class BetaMessageStream(Generic[ResponseFormatT]): text_stream: Iterator[str] """Iterator over just the text deltas in the stream. ```py for text in stream.text_stream: print(text, end="", flush=True) print() ``` """ def __init__( self, raw_stream: Stream[BetaRawMessageStreamEvent], output_format: ResponseFormatT | NotGiven, ) -> None: self._raw_stream = raw_stream self.text_stream = self.__stream_text__() self._iterator = self.__stream__() self.__final_message_snapshot: ParsedBetaMessage[ResponseFormatT] | None = None self.__output_format = output_format @property def response(self) -> httpx.Response: return self._raw_stream.response @property def request_id(self) -> str | None: return self.response.headers.get("request-id") # type: ignore[no-any-return] def __next__(self) -> ParsedBetaMessageStreamEvent[ResponseFormatT]: return self._iterator.__next__() def __iter__(self) -> Iterator[ParsedBetaMessageStreamEvent[ResponseFormatT]]: for item in self._iterator: yield item def __enter__(self) -> Self: return self def __exit__( self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None, ) -> None: self.close() def close(self) -> None: """ Close the response and release the connection. Automatically called if the response body is read to completion. """ self._raw_stream.close() def get_final_message(self) -> ParsedBetaMessage[ResponseFormatT]: """Waits until the stream has been read to completion and returns the accumulated `Message` object. """ self.until_done() assert self.__final_message_snapshot is not None return self.__final_message_snapshot def get_final_text(self) -> str: """Returns all `text` content blocks concatenated together. > [!NOTE] > Currently the API will only respond with a single content block. Will raise an error if no `text` content blocks were returned. """ message = self.get_final_message() text_blocks: list[str] = [] for block in message.content: if block.type == "text": text_blocks.append(block.text) if not text_blocks: raise RuntimeError( f".get_final_text() can only be called when the API returns a `text` content block.\nThe API returned {','.join([b.type for b in message.content])} content block type(s) that you can access by calling get_final_message().content" ) return "".join(text_blocks) def until_done(self) -> None: """Blocks until the stream has been consumed""" consume_sync_iterator(self) # properties @property def current_message_snapshot(self) -> ParsedBetaMessage[ResponseFormatT]: assert self.__final_message_snapshot is not None return self.__final_message_snapshot def __stream__(self) -> Iterator[ParsedBetaMessageStreamEvent[ResponseFormatT]]: for sse_event in self._raw_stream: self.__final_message_snapshot = accumulate_event( event=sse_event, current_snapshot=self.__final_message_snapshot, request_headers=self.response.request.headers, output_format=self.__output_format, ) events_to_fire = build_events(event=sse_event, message_snapshot=self.current_message_snapshot) for event in events_to_fire: yield event def __stream_text__(self) -> Iterator[str]: for chunk in self: if chunk.type == "content_block_delta" and chunk.delta.type == "text_delta": yield chunk.delta.text class BetaMessageStreamManager(Generic[ResponseFormatT]): """Wrapper over MessageStream that is returned by `.stream()`. ```py with client.beta.messages.stream(...) as stream: for chunk in stream: ... ``` """ def __init__( self, api_request: Callable[[], Stream[BetaRawMessageStreamEvent]], *, output_format: ResponseFormatT | NotGiven, ) -> None: self.__stream: BetaMessageStream[ResponseFormatT] | None = None self.__api_request = api_request self.__output_format = output_format def __enter__(self) -> BetaMessageStream[ResponseFormatT]: raw_stream = self.__api_request() self.__stream = BetaMessageStream(raw_stream, output_format=self.__output_format) return self.__stream def __exit__( self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None, ) -> None: if self.__stream is not None: self.__stream.close() class BetaAsyncMessageStream(Generic[ResponseFormatT]): text_stream: AsyncIterator[str] """Async iterator over just the text deltas in the stream. ```py async for text in stream.text_stream: print(text, end="", flush=True) print() ``` """ def __init__( self, raw_stream: AsyncStream[BetaRawMessageStreamEvent], output_format: ResponseFormatT | NotGiven, ) -> None: self._raw_stream = raw_stream self.text_stream = self.__stream_text__() self._iterator = self.__stream__() self.__final_message_snapshot: ParsedBetaMessage[ResponseFormatT] | None = None self.__output_format = output_format @property def response(self) -> httpx.Response: return self._raw_stream.response @property def request_id(self) -> str | None: return self.response.headers.get("request-id") # type: ignore[no-any-return] async def __anext__(self) -> ParsedBetaMessageStreamEvent[ResponseFormatT]: return await self._iterator.__anext__() async def __aiter__(self) -> AsyncIterator[ParsedBetaMessageStreamEvent[ResponseFormatT]]: async for item in self._iterator: yield item async def __aenter__(self) -> Self: return self async def __aexit__( self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None, ) -> None: await self.close() async def close(self) -> None: """ Close the response and release the connection. Automatically called if the response body is read to completion. """ await self._raw_stream.close() async def get_final_message(self) -> ParsedBetaMessage[ResponseFormatT]: """Waits until the stream has been read to completion and returns the accumulated `Message` object. """ await self.until_done() assert self.__final_message_snapshot is not None return self.__final_message_snapshot async def get_final_text(self) -> str: """Returns all `text` content blocks concatenated together. > [!NOTE] > Currently the API will only respond with a single content block. Will raise an error if no `text` content blocks were returned. """ message = await self.get_final_message() text_blocks: list[str] = [] for block in message.content: if block.type == "text": text_blocks.append(block.text) if not text_blocks: raise RuntimeError( f".get_final_text() can only be called when the API returns a `text` content block.\nThe API returned {','.join([b.type for b in message.content])} content block type(s) that you can access by calling get_final_message().content" ) return "".join(text_blocks) async def until_done(self) -> None: """Waits until the stream has been consumed""" await consume_async_iterator(self) # properties @property def current_message_snapshot(self) -> ParsedBetaMessage[ResponseFormatT]: assert self.__final_message_snapshot is not None return self.__final_message_snapshot async def __stream__(self) -> AsyncIterator[ParsedBetaMessageStreamEvent[ResponseFormatT]]: async for sse_event in self._raw_stream: self.__final_message_snapshot = accumulate_event( event=sse_event, current_snapshot=self.__final_message_snapshot, request_headers=self.response.request.headers, output_format=self.__output_format, ) events_to_fire = build_events(event=sse_event, message_snapshot=self.current_message_snapshot) for event in events_to_fire: yield event async def __stream_text__(self) -> AsyncIterator[str]: async for chunk in self: if chunk.type == "content_block_delta" and chunk.delta.type == "text_delta": yield chunk.delta.text class BetaAsyncMessageStreamManager(Generic[ResponseFormatT]): """Wrapper over BetaAsyncMessageStream that is returned by `.stream()` so that an async context manager can be used without `await`ing the original client call. ```py async with client.beta.messages.stream(...) as stream: async for chunk in stream: ... ``` """ def __init__( self, api_request: Awaitable[AsyncStream[BetaRawMessageStreamEvent]], *, output_format: ResponseFormatT | NotGiven = NOT_GIVEN, ) -> None: self.__stream: BetaAsyncMessageStream[ResponseFormatT] | None = None self.__api_request = api_request self.__output_format = output_format async def __aenter__(self) -> BetaAsyncMessageStream[ResponseFormatT]: raw_stream = await self.__api_request self.__stream = BetaAsyncMessageStream(raw_stream, output_format=self.__output_format) return self.__stream async def __aexit__( self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None, ) -> None: if self.__stream is not None: await self.__stream.close() def build_events( *, event: BetaRawMessageStreamEvent, message_snapshot: ParsedBetaMessage[ResponseFormatT], ) -> list[ParsedBetaMessageStreamEvent[ResponseFormatT]]: events_to_fire: list[ParsedBetaMessageStreamEvent[ResponseFormatT]] = [] if event.type == "message_start": events_to_fire.append(event) elif event.type == "message_delta": events_to_fire.append(event) elif event.type == "message_stop": events_to_fire.append( build(ParsedBetaMessageStopEvent[ResponseFormatT], type="message_stop", message=message_snapshot) ) elif event.type == "content_block_start": events_to_fire.append(event) elif event.type == "content_block_delta": events_to_fire.append(event) content_block = message_snapshot.content[event.index] if event.delta.type == "text_delta": if content_block.type == "text": events_to_fire.append( build( ParsedBetaTextEvent, type="text", text=event.delta.text, snapshot=content_block.text, ) ) elif event.delta.type == "input_json_delta": if content_block.type == "tool_use" or content_block.type == "mcp_tool_use": events_to_fire.append( build( BetaInputJsonEvent, type="input_json", partial_json=event.delta.partial_json, snapshot=content_block.input, ) ) elif event.delta.type == "citations_delta": if content_block.type == "text": events_to_fire.append( build( BetaCitationEvent, type="citation", citation=event.delta.citation, snapshot=content_block.citations or [], ) ) elif event.delta.type == "thinking_delta": if content_block.type == "thinking": events_to_fire.append( build( BetaThinkingEvent, type="thinking", thinking=event.delta.thinking, snapshot=content_block.thinking, ) ) elif event.delta.type == "signature_delta": if content_block.type == "thinking": events_to_fire.append( build( BetaSignatureEvent, type="signature", signature=content_block.signature, ) ) pass elif event.delta.type == "compaction_delta": if content_block.type == "compaction": events_to_fire.append( build( BetaCompactionEvent, type="compaction", content=content_block.content, encrypted_content=content_block.encrypted_content, ) ) else: # we only want exhaustive checking for linters, not at runtime if TYPE_CHECKING: # type: ignore[unreachable] assert_never(event.delta) elif event.type == "content_block_stop": content_block = message_snapshot.content[event.index] event_to_fire = build( ParsedBetaContentBlockStopEvent, type="content_block_stop", index=event.index, content_block=content_block, ) events_to_fire.append(event_to_fire) else: # we only want exhaustive checking for linters, not at runtime if TYPE_CHECKING: # type: ignore[unreachable] assert_never(event) return events_to_fire JSON_BUF_PROPERTY = "__json_buf" TRACKS_TOOL_INPUT = ( BetaToolUseBlock, BetaServerToolUseBlock, BetaMCPToolUseBlock, ) def accumulate_event( *, event: BetaRawMessageStreamEvent, current_snapshot: ParsedBetaMessage[ResponseFormatT] | None, request_headers: httpx.Headers, output_format: ResponseFormatT | NotGiven = NOT_GIVEN, ) -> ParsedBetaMessage[ResponseFormatT]: if not isinstance(cast(Any, event), BaseModel): event = cast( # pyright: ignore[reportUnnecessaryCast] BetaRawMessageStreamEvent, construct_type_unchecked( type_=cast(Type[BetaRawMessageStreamEvent], BetaRawMessageStreamEvent), value=event, ), ) if not isinstance(cast(Any, event), BaseModel): raise TypeError( f"Unexpected event runtime type, after deserialising twice - {event} - {builtins.type(event)}" ) if current_snapshot is None: if event.type == "message_start": return cast( ParsedBetaMessage[ResponseFormatT], ParsedBetaMessage.construct(**cast(Any, event.message.to_dict())) ) raise RuntimeError(f'Unexpected event order, got {event.type} before "message_start"') if event.type == "content_block_start": # TODO: check index current_snapshot.content.append( cast( Any, # Pydantic does not support generic unions at runtime construct_type(type_=ParsedBetaContentBlock, value=event.content_block.to_dict()), ), ) if event.content_block.type == "fallback": # the final hop's fallback block names the model that served the response — # keeps the snapshot consistent with the relabeled non-streaming message current_snapshot.model = event.content_block.to.model elif event.type == "content_block_delta": content = current_snapshot.content[event.index] if event.delta.type == "text_delta": if content.type == "text": content.text += event.delta.text elif event.delta.type == "input_json_delta": if isinstance(content, TRACKS_TOOL_INPUT): from jiter import from_json # we need to keep track of the raw JSON string as well so that we can # re-parse it for each delta, for now we just store it as an untyped # property on the snapshot json_buf = cast(bytes, getattr(content, JSON_BUF_PROPERTY, b"")) json_buf += bytes(event.delta.partial_json, "utf-8") if json_buf: try: anthropic_beta = request_headers.get("anthropic-beta", "") if request_headers else "" if "fine-grained-tool-streaming-2025-05-14" in anthropic_beta: content.input = from_json(json_buf, partial_mode="trailing-strings") else: content.input = from_json(json_buf, partial_mode=True) except ValueError as e: raise ValueError( f"Unable to parse tool parameter JSON from model. Please retry your request or adjust your prompt. Error: {e}. JSON: {json_buf.decode('utf-8')}" ) from e setattr(content, JSON_BUF_PROPERTY, json_buf) elif event.delta.type == "citations_delta": if content.type == "text": if not content.citations: content.citations = [event.delta.citation] else: content.citations.append(event.delta.citation) elif event.delta.type == "thinking_delta": if content.type == "thinking": content.thinking += event.delta.thinking elif event.delta.type == "signature_delta": if content.type == "thinking": content.signature = event.delta.signature elif event.delta.type == "compaction_delta": if content.type == "compaction": content.content = event.delta.content content.encrypted_content = event.delta.encrypted_content else: # we only want exhaustive checking for linters, not at runtime if TYPE_CHECKING: # type: ignore[unreachable] assert_never(event.delta) elif event.type == "content_block_stop": content_block = current_snapshot.content[event.index] if content_block.type == "text" and is_given(output_format): content_block.parsed_output = parse_text(content_block.text, output_format) elif event.type == "message_delta": current_snapshot.container = event.delta.container current_snapshot.stop_reason = event.delta.stop_reason current_snapshot.stop_sequence = event.delta.stop_sequence if event.delta.stop_details is not None: current_snapshot.stop_details = event.delta.stop_details current_snapshot.usage.output_tokens = event.usage.output_tokens current_snapshot.context_management = event.context_management # Update other usage fields if they exist in the event if event.usage.input_tokens is not None: current_snapshot.usage.input_tokens = event.usage.input_tokens if event.usage.cache_creation_input_tokens is not None: current_snapshot.usage.cache_creation_input_tokens = event.usage.cache_creation_input_tokens if event.usage.cache_read_input_tokens is not None: current_snapshot.usage.cache_read_input_tokens = event.usage.cache_read_input_tokens if event.usage.server_tool_use is not None: current_snapshot.usage.server_tool_use = event.usage.server_tool_use if event.usage.iterations is not None: current_snapshot.usage.iterations = event.usage.iterations if event.usage.fallback_credit is not None: current_snapshot.usage.fallback_credit = event.usage.fallback_credit return current_snapshot anthropic-sdk-python-0.120.2/src/anthropic/lib/streaming/_beta_types.py000066400000000000000000000057271523216435200261440ustar00rootroot00000000000000from typing import TYPE_CHECKING, Any, Dict, Union, Generic, cast from typing_extensions import List, Literal, Annotated import jiter from ..._models import BaseModel, GenericModel from ...types.beta import ( BetaRawMessageStopEvent, BetaRawMessageDeltaEvent, BetaRawMessageStartEvent, BetaRawContentBlockStopEvent, BetaRawContentBlockDeltaEvent, BetaRawContentBlockStartEvent, ) from .._parse._response import ResponseFormatT from ..._utils._transform import PropertyInfo from ...types.beta.parsed_beta_message import ParsedBetaMessage, ParsedBetaContentBlock from ...types.beta.beta_citations_delta import Citation class ParsedBetaTextEvent(BaseModel): type: Literal["text"] text: str """The text delta""" snapshot: str """The entire accumulated text""" def parsed_snapshot(self) -> Dict[str, Any]: return cast(Dict[str, Any], jiter.from_json(self.snapshot.encode("utf-8"), partial_mode="trailing-strings")) class BetaCitationEvent(BaseModel): type: Literal["citation"] citation: Citation """The new citation""" snapshot: List[Citation] """All of the accumulated citations""" class BetaThinkingEvent(BaseModel): type: Literal["thinking"] thinking: str """The thinking delta""" snapshot: str """The accumulated thinking so far""" class BetaSignatureEvent(BaseModel): type: Literal["signature"] signature: str """The signature of the thinking block""" class BetaInputJsonEvent(BaseModel): type: Literal["input_json"] partial_json: str """A partial JSON string delta e.g. `'"San Francisco,'` """ snapshot: object """The currently accumulated parsed object. e.g. `{'location': 'San Francisco, CA'}` """ class BetaCompactionEvent(BaseModel): type: Literal["compaction"] content: Union[str, None] """The compaction content""" encrypted_content: Union[str, None] """Opaque metadata from prior compaction, to be round-tripped verbatim""" class ParsedBetaMessageStopEvent(BetaRawMessageStopEvent, GenericModel, Generic[ResponseFormatT]): type: Literal["message_stop"] message: ParsedBetaMessage[ResponseFormatT] class ParsedBetaContentBlockStopEvent(BetaRawContentBlockStopEvent, GenericModel, Generic[ResponseFormatT]): type: Literal["content_block_stop"] if TYPE_CHECKING: content_block: ParsedBetaContentBlock[ResponseFormatT] else: content_block: ParsedBetaContentBlock ParsedBetaMessageStreamEvent = Annotated[ Union[ ParsedBetaTextEvent, BetaCitationEvent, BetaThinkingEvent, BetaSignatureEvent, BetaInputJsonEvent, BetaCompactionEvent, BetaRawMessageStartEvent, BetaRawMessageDeltaEvent, ParsedBetaMessageStopEvent[ResponseFormatT], BetaRawContentBlockStartEvent, BetaRawContentBlockDeltaEvent, ParsedBetaContentBlockStopEvent[ResponseFormatT], ], PropertyInfo(discriminator="type"), ] anthropic-sdk-python-0.120.2/src/anthropic/lib/streaming/_messages.py000066400000000000000000000451351523216435200256110ustar00rootroot00000000000000from __future__ import annotations from types import TracebackType from typing import TYPE_CHECKING, Any, Type, Generic, Callable, cast from typing_extensions import Self, Iterator, Awaitable, AsyncIterator, assert_never import httpx from pydantic import BaseModel from anthropic.types.tool_use_block import ToolUseBlock from anthropic.types.server_tool_use_block import ServerToolUseBlock from ._types import ( TextEvent, CitationEvent, ThinkingEvent, InputJsonEvent, SignatureEvent, ParsedMessageStopEvent, ParsedMessageStreamEvent, ParsedContentBlockStopEvent, ) from ...types import RawMessageStreamEvent from ..._types import NOT_GIVEN, NotGiven from ..._utils import consume_sync_iterator, consume_async_iterator from ..._models import build, construct_type, construct_type_unchecked from ..._streaming import Stream, AsyncStream from ..._utils._utils import is_given from .._parse._response import ResponseFormatT, parse_text from ...types.parsed_message import ParsedMessage, ParsedContentBlock class MessageStream(Generic[ResponseFormatT]): text_stream: Iterator[str] """Iterator over just the text deltas in the stream. ```py for text in stream.text_stream: print(text, end="", flush=True) print() ``` """ def __init__( self, raw_stream: Stream[RawMessageStreamEvent], output_format: ResponseFormatT | NotGiven, ) -> None: self._raw_stream = raw_stream self.text_stream = self.__stream_text__() self._iterator = self.__stream__() self.__final_message_snapshot: ParsedMessage[ResponseFormatT] | None = None self.__output_format = output_format @property def response(self) -> httpx.Response: return self._raw_stream.response @property def request_id(self) -> str | None: return self.response.headers.get("request-id") # type: ignore[no-any-return] def __next__(self) -> ParsedMessageStreamEvent[ResponseFormatT]: return self._iterator.__next__() def __iter__(self) -> Iterator[ParsedMessageStreamEvent[ResponseFormatT]]: for item in self._iterator: yield item def __enter__(self) -> Self: return self def __exit__( self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None, ) -> None: self.close() def close(self) -> None: """ Close the response and release the connection. Automatically called if the response body is read to completion. """ self._raw_stream.close() def get_final_message(self) -> ParsedMessage[ResponseFormatT]: """Waits until the stream has been read to completion and returns the accumulated `Message` object. """ self.until_done() assert self.__final_message_snapshot is not None return self.__final_message_snapshot def get_final_text(self) -> str: """Returns all `text` content blocks concatenated together. > [!NOTE] > Currently the API will only respond with a single content block. Will raise an error if no `text` content blocks were returned. """ message = self.get_final_message() text_blocks: list[str] = [] for block in message.content: if block.type == "text": text_blocks.append(block.text) if not text_blocks: raise RuntimeError( f".get_final_text() can only be called when the API returns a `text` content block.\nThe API returned {','.join([b.type for b in message.content])} content block type(s) that you can access by calling get_final_message().content" ) return "".join(text_blocks) def until_done(self) -> None: """Blocks until the stream has been consumed""" consume_sync_iterator(self) # properties @property def current_message_snapshot(self) -> ParsedMessage[ResponseFormatT]: assert self.__final_message_snapshot is not None return self.__final_message_snapshot def __stream__(self) -> Iterator[ParsedMessageStreamEvent[ResponseFormatT]]: for sse_event in self._raw_stream: self.__final_message_snapshot = accumulate_event( event=sse_event, current_snapshot=self.__final_message_snapshot, output_format=self.__output_format, ) events_to_fire = build_events(event=sse_event, message_snapshot=self.current_message_snapshot) for event in events_to_fire: yield event def __stream_text__(self) -> Iterator[str]: for chunk in self: if chunk.type == "content_block_delta" and chunk.delta.type == "text_delta": yield chunk.delta.text class MessageStreamManager(Generic[ResponseFormatT]): """Wrapper over MessageStream that is returned by `.stream()`. ```py with client.messages.stream(...) as stream: for chunk in stream: ... ``` """ def __init__( self, api_request: Callable[[], Stream[RawMessageStreamEvent]], *, output_format: ResponseFormatT | NotGiven, ) -> None: self.__stream: MessageStream[ResponseFormatT] | None = None self.__api_request = api_request self.__output_format = output_format def __enter__(self) -> MessageStream[ResponseFormatT]: raw_stream = self.__api_request() self.__stream = MessageStream(raw_stream, output_format=self.__output_format) return self.__stream def __exit__( self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None, ) -> None: if self.__stream is not None: self.__stream.close() class AsyncMessageStream(Generic[ResponseFormatT]): text_stream: AsyncIterator[str] """Async iterator over just the text deltas in the stream. ```py async for text in stream.text_stream: print(text, end="", flush=True) print() ``` """ def __init__( self, raw_stream: AsyncStream[RawMessageStreamEvent], output_format: ResponseFormatT | NotGiven, ) -> None: self._raw_stream = raw_stream self.text_stream = self.__stream_text__() self._iterator = self.__stream__() self.__final_message_snapshot: ParsedMessage[ResponseFormatT] | None = None self.__output_format = output_format @property def response(self) -> httpx.Response: return self._raw_stream.response @property def request_id(self) -> str | None: return self.response.headers.get("request-id") # type: ignore[no-any-return] async def __anext__(self) -> ParsedMessageStreamEvent[ResponseFormatT]: return await self._iterator.__anext__() async def __aiter__(self) -> AsyncIterator[ParsedMessageStreamEvent[ResponseFormatT]]: async for item in self._iterator: yield item async def __aenter__(self) -> Self: return self async def __aexit__( self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None, ) -> None: await self.close() async def close(self) -> None: """ Close the response and release the connection. Automatically called if the response body is read to completion. """ await self._raw_stream.close() async def get_final_message(self) -> ParsedMessage[ResponseFormatT]: """Waits until the stream has been read to completion and returns the accumulated `Message` object. """ await self.until_done() assert self.__final_message_snapshot is not None return self.__final_message_snapshot async def get_final_text(self) -> str: """Returns all `text` content blocks concatenated together. > [!NOTE] > Currently the API will only respond with a single content block. Will raise an error if no `text` content blocks were returned. """ message = await self.get_final_message() text_blocks: list[str] = [] for block in message.content: if block.type == "text": text_blocks.append(block.text) if not text_blocks: raise RuntimeError( f".get_final_text() can only be called when the API returns a `text` content block.\nThe API returned {','.join([b.type for b in message.content])} content block type(s) that you can access by calling get_final_message().content" ) return "".join(text_blocks) async def until_done(self) -> None: """Waits until the stream has been consumed""" await consume_async_iterator(self) # properties @property def current_message_snapshot(self) -> ParsedMessage[ResponseFormatT]: assert self.__final_message_snapshot is not None return self.__final_message_snapshot async def __stream__(self) -> AsyncIterator[ParsedMessageStreamEvent[ResponseFormatT]]: async for sse_event in self._raw_stream: self.__final_message_snapshot = accumulate_event( event=sse_event, current_snapshot=self.__final_message_snapshot, output_format=self.__output_format, ) events_to_fire = build_events(event=sse_event, message_snapshot=self.current_message_snapshot) for event in events_to_fire: yield event async def __stream_text__(self) -> AsyncIterator[str]: async for chunk in self: if chunk.type == "content_block_delta" and chunk.delta.type == "text_delta": yield chunk.delta.text class AsyncMessageStreamManager(Generic[ResponseFormatT]): """Wrapper over AsyncMessageStream that is returned by `.stream()` so that an async context manager can be used without `await`ing the original client call. ```py async with client.messages.stream(...) as stream: async for chunk in stream: ... ``` """ def __init__( self, api_request: Awaitable[AsyncStream[RawMessageStreamEvent]], *, output_format: ResponseFormatT | NotGiven = NOT_GIVEN, ) -> None: self.__stream: AsyncMessageStream[ResponseFormatT] | None = None self.__api_request = api_request self.__output_format = output_format async def __aenter__(self) -> AsyncMessageStream[ResponseFormatT]: raw_stream = await self.__api_request self.__stream = AsyncMessageStream(raw_stream, output_format=self.__output_format) return self.__stream async def __aexit__( self, exc_type: type[BaseException] | None, exc: BaseException | None, exc_tb: TracebackType | None, ) -> None: if self.__stream is not None: await self.__stream.close() def build_events( *, event: RawMessageStreamEvent, message_snapshot: ParsedMessage[ResponseFormatT], ) -> list[ParsedMessageStreamEvent[ResponseFormatT]]: events_to_fire: list[ParsedMessageStreamEvent[ResponseFormatT]] = [] if event.type == "message_start": events_to_fire.append(event) elif event.type == "message_delta": events_to_fire.append(event) elif event.type == "message_stop": events_to_fire.append( build(ParsedMessageStopEvent[ResponseFormatT], type="message_stop", message=message_snapshot) ) elif event.type == "content_block_start": events_to_fire.append(event) elif event.type == "content_block_delta": events_to_fire.append(event) content_block = message_snapshot.content[event.index] if event.delta.type == "text_delta": if content_block.type == "text": events_to_fire.append( build( TextEvent, type="text", text=event.delta.text, snapshot=content_block.text, ) ) elif event.delta.type == "input_json_delta": if content_block.type == "tool_use": events_to_fire.append( build( InputJsonEvent, type="input_json", partial_json=event.delta.partial_json, snapshot=content_block.input, ) ) elif event.delta.type == "citations_delta": if content_block.type == "text": events_to_fire.append( build( CitationEvent, type="citation", citation=event.delta.citation, snapshot=content_block.citations or [], ) ) elif event.delta.type == "thinking_delta": if content_block.type == "thinking": events_to_fire.append( build( ThinkingEvent, type="thinking", thinking=event.delta.thinking, snapshot=content_block.thinking, ) ) elif event.delta.type == "signature_delta": if content_block.type == "thinking": events_to_fire.append( build( SignatureEvent, type="signature", signature=content_block.signature, ) ) pass else: # we only want exhaustive checking for linters, not at runtime if TYPE_CHECKING: # type: ignore[unreachable] assert_never(event.delta) elif event.type == "content_block_stop": content_block = message_snapshot.content[event.index] event_to_fire = build( ParsedContentBlockStopEvent, type="content_block_stop", index=event.index, content_block=content_block, ) events_to_fire.append(event_to_fire) else: # we only want exhaustive checking for linters, not at runtime if TYPE_CHECKING: # type: ignore[unreachable] assert_never(event) return events_to_fire JSON_BUF_PROPERTY = "__json_buf" TRACKS_TOOL_INPUT = ( ToolUseBlock, ServerToolUseBlock, ) def accumulate_event( *, event: RawMessageStreamEvent, current_snapshot: ParsedMessage[ResponseFormatT] | None, output_format: ResponseFormatT | NotGiven = NOT_GIVEN, ) -> ParsedMessage[ResponseFormatT]: if not isinstance(cast(Any, event), BaseModel): event = cast( # pyright: ignore[reportUnnecessaryCast] RawMessageStreamEvent, construct_type_unchecked( type_=cast(Type[RawMessageStreamEvent], RawMessageStreamEvent), value=event, ), ) if not isinstance(cast(Any, event), BaseModel): raise TypeError(f"Unexpected event runtime type, after deserialising twice - {event} - {type(event)}") if current_snapshot is None: if event.type == "message_start": return cast(ParsedMessage[ResponseFormatT], ParsedMessage.construct(**cast(Any, event.message.to_dict()))) raise RuntimeError(f'Unexpected event order, got {event.type} before "message_start"') if event.type == "content_block_start": # TODO: check index current_snapshot.content.append( cast( Any, # Pydantic does not support generic unions at runtime construct_type(type_=ParsedContentBlock, value=event.content_block.model_dump()), ), ) elif event.type == "content_block_delta": content = current_snapshot.content[event.index] if event.delta.type == "text_delta": if content.type == "text": content.text += event.delta.text elif event.delta.type == "input_json_delta": if isinstance(content, TRACKS_TOOL_INPUT): from jiter import from_json # we need to keep track of the raw JSON string as well so that we can # re-parse it for each delta, for now we just store it as an untyped # property on the snapshot json_buf = cast(bytes, getattr(content, JSON_BUF_PROPERTY, b"")) json_buf += bytes(event.delta.partial_json, "utf-8") if json_buf: content.input = from_json(json_buf, partial_mode=True) setattr(content, JSON_BUF_PROPERTY, json_buf) elif event.delta.type == "citations_delta": if content.type == "text": if not content.citations: content.citations = [event.delta.citation] else: content.citations.append(event.delta.citation) elif event.delta.type == "thinking_delta": if content.type == "thinking": content.thinking += event.delta.thinking elif event.delta.type == "signature_delta": if content.type == "thinking": content.signature = event.delta.signature else: # we only want exhaustive checking for linters, not at runtime if TYPE_CHECKING: # type: ignore[unreachable] assert_never(event.delta) elif event.type == "content_block_stop": content_block = current_snapshot.content[event.index] if content_block.type == "text" and is_given(output_format): content_block.parsed_output = parse_text(content_block.text, output_format) elif event.type == "message_delta": current_snapshot.stop_reason = event.delta.stop_reason current_snapshot.stop_sequence = event.delta.stop_sequence if event.delta.stop_details is not None: current_snapshot.stop_details = event.delta.stop_details current_snapshot.usage.output_tokens = event.usage.output_tokens # Update other usage fields if they exist in the event if event.usage.input_tokens is not None: current_snapshot.usage.input_tokens = event.usage.input_tokens if event.usage.cache_creation_input_tokens is not None: current_snapshot.usage.cache_creation_input_tokens = event.usage.cache_creation_input_tokens if event.usage.cache_read_input_tokens is not None: current_snapshot.usage.cache_read_input_tokens = event.usage.cache_read_input_tokens if event.usage.server_tool_use is not None: current_snapshot.usage.server_tool_use = event.usage.server_tool_use return current_snapshot anthropic-sdk-python-0.120.2/src/anthropic/lib/streaming/_types.py000066400000000000000000000064061523216435200251440ustar00rootroot00000000000000from typing import TYPE_CHECKING, Any, Dict, Union, Generic, cast from typing_extensions import List, Literal, Annotated import jiter from ...types import ( Message, ContentBlock, MessageDeltaEvent as RawMessageDeltaEvent, MessageStartEvent as RawMessageStartEvent, RawMessageStopEvent, ContentBlockDeltaEvent as RawContentBlockDeltaEvent, ContentBlockStartEvent as RawContentBlockStartEvent, RawContentBlockStopEvent, ) from ..._models import BaseModel, GenericModel from .._parse._response import ResponseFormatT from ..._utils._transform import PropertyInfo from ...types.parsed_message import ParsedMessage, ParsedContentBlock from ...types.citations_delta import Citation class TextEvent(BaseModel): type: Literal["text"] text: str """The text delta""" snapshot: str """The entire accumulated text""" def parsed_snapshot(self) -> Dict[str, Any]: return cast(Dict[str, Any], jiter.from_json(self.snapshot.encode("utf-8"), partial_mode="trailing-strings")) class CitationEvent(BaseModel): type: Literal["citation"] citation: Citation """The new citation""" snapshot: List[Citation] """All of the accumulated citations""" class ThinkingEvent(BaseModel): type: Literal["thinking"] thinking: str """The thinking delta""" snapshot: str """The accumulated thinking so far""" class SignatureEvent(BaseModel): type: Literal["signature"] signature: str """The signature of the thinking block""" class InputJsonEvent(BaseModel): type: Literal["input_json"] partial_json: str """A partial JSON string delta e.g. `'"San Francisco,'` """ snapshot: object """The currently accumulated parsed object. e.g. `{'location': 'San Francisco, CA'}` """ class MessageStopEvent(RawMessageStopEvent): type: Literal["message_stop"] message: Message class ContentBlockStopEvent(RawContentBlockStopEvent): type: Literal["content_block_stop"] content_block: ContentBlock MessageStreamEvent = Annotated[ Union[ TextEvent, CitationEvent, ThinkingEvent, SignatureEvent, InputJsonEvent, RawMessageStartEvent, RawMessageDeltaEvent, MessageStopEvent, RawContentBlockStartEvent, RawContentBlockDeltaEvent, ContentBlockStopEvent, ], PropertyInfo(discriminator="type"), ] class ParsedMessageStopEvent(RawMessageStopEvent, GenericModel, Generic[ResponseFormatT]): type: Literal["message_stop"] message: ParsedMessage[ResponseFormatT] class ParsedContentBlockStopEvent(RawContentBlockStopEvent, GenericModel, Generic[ResponseFormatT]): type: Literal["content_block_stop"] if TYPE_CHECKING: content_block: ParsedContentBlock[ResponseFormatT] else: content_block: ParsedContentBlock ParsedMessageStreamEvent = Annotated[ Union[ TextEvent, CitationEvent, ThinkingEvent, SignatureEvent, InputJsonEvent, RawMessageStartEvent, RawMessageDeltaEvent, ParsedMessageStopEvent[ResponseFormatT], RawContentBlockStartEvent, RawContentBlockDeltaEvent, ParsedContentBlockStopEvent[ResponseFormatT], ], PropertyInfo(discriminator="type"), ] anthropic-sdk-python-0.120.2/src/anthropic/lib/tools/000077500000000000000000000000001523216435200224305ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/lib/tools/__init__.py000066400000000000000000000015131523216435200245410ustar00rootroot00000000000000from ._beta_runner import BetaToolRunner, BetaAsyncToolRunner, BetaStreamingToolRunner, BetaAsyncStreamingToolRunner from ._beta_functions import ( ToolError, BetaFunctionTool, BetaAsyncFunctionTool, BetaBuiltinFunctionTool, BetaFunctionToolResultType, BetaAsyncBuiltinFunctionTool, beta_tool, beta_async_tool, ) from ._beta_builtin_memory_tool import BetaAbstractMemoryTool, BetaAsyncAbstractMemoryTool __all__ = [ "beta_tool", "beta_async_tool", "BetaFunctionTool", "BetaAsyncFunctionTool", "BetaBuiltinFunctionTool", "BetaAsyncBuiltinFunctionTool", "BetaToolRunner", "BetaAsyncStreamingToolRunner", "BetaStreamingToolRunner", "BetaAsyncToolRunner", "BetaFunctionToolResultType", "BetaAbstractMemoryTool", "BetaAsyncAbstractMemoryTool", "ToolError", ] anthropic-sdk-python-0.120.2/src/anthropic/lib/tools/_beta_builtin_memory_tool.py000066400000000000000000001046451523216435200302410ustar00rootroot00000000000000from __future__ import annotations import os import uuid import shutil from abc import abstractmethod from typing import TYPE_CHECKING, Any, List, cast from pathlib import Path from typing_extensions import override, assert_never from anyio import Path as AsyncPath from anyio.to_thread import run_sync from anthropic.types.beta import ( BetaMemoryTool20250818ViewCommand, BetaMemoryTool20250818CreateCommand, BetaMemoryTool20250818DeleteCommand, BetaMemoryTool20250818InsertCommand, BetaMemoryTool20250818RenameCommand, BetaMemoryTool20250818StrReplaceCommand, ) from ..._models import construct_type_unchecked from ...types.beta import ( BetaMemoryTool20250818Param, BetaMemoryTool20250818Command, BetaCacheControlEphemeralParam, BetaMemoryTool20250818ViewCommand, BetaMemoryTool20250818CreateCommand, BetaMemoryTool20250818DeleteCommand, BetaMemoryTool20250818InsertCommand, BetaMemoryTool20250818RenameCommand, BetaMemoryTool20250818StrReplaceCommand, ) from ._beta_functions import ( ToolError, BetaBuiltinFunctionTool, BetaFunctionToolResultType, BetaAsyncBuiltinFunctionTool, ) MAX_LINES = 999999 LINE_NUMBER_WIDTH = len(str(MAX_LINES)) # Owner read/write only. Avoids 0o666 which, in environments with a permissive # umask (e.g. Docker where umask is often 0o000), would make memory files # world-readable or even world-writable. _FILE_CREATE_MODE = 0o600 # The default mkdir mode is 0o777, but we want to be more restrictive for memory # directories to avoid them being world-accessible in environments with permissive umasks # (eg Docker) _DIR_CREATE_MODE = 0o700 class BetaAbstractMemoryTool(BetaBuiltinFunctionTool): """Abstract base class for memory tool implementations. This class provides the interface for implementing a custom memory backend for Claude. Subclass this to create your own memory storage solution (e.g., database, cloud storage, encrypted files, etc.). Example usage: ```py class MyMemoryTool(BetaAbstractMemoryTool): def view(self, command: BetaMemoryTool20250818ViewCommand) -> BetaFunctionToolResultType: ... return "view result" def create(self, command: BetaMemoryTool20250818CreateCommand) -> BetaFunctionToolResultType: ... return "created successfully" # ... implement other abstract methods client = Anthropic() memory_tool = MyMemoryTool() message = client.beta.messages.run_tools( model="claude-sonnet-4-5", messages=[{"role": "user", "content": "Remember that I like coffee"}], tools=[memory_tool], ).until_done() ``` """ def __init__(self, *, cache_control: BetaCacheControlEphemeralParam | None = None) -> None: super().__init__() self._cache_control = cache_control @override def to_dict(self) -> BetaMemoryTool20250818Param: param: BetaMemoryTool20250818Param = {"type": "memory_20250818", "name": "memory"} if self._cache_control is not None: param["cache_control"] = self._cache_control return param @override def call(self, input: object) -> BetaFunctionToolResultType: command = cast( BetaMemoryTool20250818Command, construct_type_unchecked(value=input, type_=cast(Any, BetaMemoryTool20250818Command)), ) return self.execute(command) def execute(self, command: BetaMemoryTool20250818Command) -> BetaFunctionToolResultType: """Execute a memory command and return the result. This method dispatches to the appropriate handler method based on the command type (view, create, str_replace, insert, delete, rename). You typically don't need to override this method. """ if command.command == "view": return self.view(command) elif command.command == "create": return self.create(command) elif command.command == "str_replace": return self.str_replace(command) elif command.command == "insert": return self.insert(command) elif command.command == "delete": return self.delete(command) elif command.command == "rename": return self.rename(command) elif TYPE_CHECKING: # type: ignore[unreachable] assert_never(command) else: raise NotImplementedError(f"Unknown command: {command.command}") @abstractmethod def view(self, command: BetaMemoryTool20250818ViewCommand) -> BetaFunctionToolResultType: """View the contents of a memory path.""" pass @abstractmethod def create(self, command: BetaMemoryTool20250818CreateCommand) -> BetaFunctionToolResultType: """Create a new memory file with the specified content.""" pass @abstractmethod def str_replace(self, command: BetaMemoryTool20250818StrReplaceCommand) -> BetaFunctionToolResultType: """Replace text in a memory file.""" pass @abstractmethod def insert(self, command: BetaMemoryTool20250818InsertCommand) -> BetaFunctionToolResultType: """Insert text at a specific line number in a memory file.""" pass @abstractmethod def delete(self, command: BetaMemoryTool20250818DeleteCommand) -> BetaFunctionToolResultType: """Delete a memory file or directory.""" pass @abstractmethod def rename(self, command: BetaMemoryTool20250818RenameCommand) -> BetaFunctionToolResultType: """Rename or move a memory file or directory.""" pass def clear_all_memory(self) -> BetaFunctionToolResultType: """Clear all memory data.""" raise NotImplementedError("clear_all_memory not implemented") class BetaAsyncAbstractMemoryTool(BetaAsyncBuiltinFunctionTool): """Abstract base class for memory tool implementations. This class provides the interface for implementing a custom memory backend for Claude. Subclass this to create your own memory storage solution (e.g., database, cloud storage, encrypted files, etc.). Example usage: ```py class MyMemoryTool(BetaAbstractMemoryTool): def view(self, command: BetaMemoryTool20250818ViewCommand) -> BetaFunctionToolResultType: ... return "view result" def create(self, command: BetaMemoryTool20250818CreateCommand) -> BetaFunctionToolResultType: ... return "created successfully" # ... implement other abstract methods client = Anthropic() memory_tool = MyMemoryTool() message = client.beta.messages.run_tools( model="claude-sonnet-4-5", messages=[{"role": "user", "content": "Remember that I like coffee"}], tools=[memory_tool], ).until_done() ``` """ def __init__(self, *, cache_control: BetaCacheControlEphemeralParam | None = None) -> None: super().__init__() self._cache_control = cache_control @override def to_dict(self) -> BetaMemoryTool20250818Param: param: BetaMemoryTool20250818Param = {"type": "memory_20250818", "name": "memory"} if self._cache_control is not None: param["cache_control"] = self._cache_control return param @override async def call(self, input: object) -> BetaFunctionToolResultType: command = cast( BetaMemoryTool20250818Command, construct_type_unchecked(value=input, type_=cast(Any, BetaMemoryTool20250818Command)), ) return await self.execute(command) async def execute(self, command: BetaMemoryTool20250818Command) -> BetaFunctionToolResultType: """Execute a memory command and return the result. This method dispatches to the appropriate handler method based on the command type (view, create, str_replace, insert, delete, rename). You typically don't need to override this method. """ if command.command == "view": return await self.view(command) elif command.command == "create": return await self.create(command) elif command.command == "str_replace": return await self.str_replace(command) elif command.command == "insert": return await self.insert(command) elif command.command == "delete": return await self.delete(command) elif command.command == "rename": return await self.rename(command) elif TYPE_CHECKING: # type: ignore[unreachable] assert_never(command) else: raise NotImplementedError(f"Unknown command: {command.command}") @abstractmethod async def view(self, command: BetaMemoryTool20250818ViewCommand) -> BetaFunctionToolResultType: """View the contents of a memory path.""" pass @abstractmethod async def create(self, command: BetaMemoryTool20250818CreateCommand) -> BetaFunctionToolResultType: """Create a new memory file with the specified content.""" pass @abstractmethod async def str_replace(self, command: BetaMemoryTool20250818StrReplaceCommand) -> BetaFunctionToolResultType: """Replace text in a memory file.""" pass @abstractmethod async def insert(self, command: BetaMemoryTool20250818InsertCommand) -> BetaFunctionToolResultType: """Insert text at a specific line number in a memory file.""" pass @abstractmethod async def delete(self, command: BetaMemoryTool20250818DeleteCommand) -> BetaFunctionToolResultType: """Delete a memory file or directory.""" pass @abstractmethod async def rename(self, command: BetaMemoryTool20250818RenameCommand) -> BetaFunctionToolResultType: """Rename or move a memory file or directory.""" pass async def clear_all_memory(self) -> BetaFunctionToolResultType: """Clear all memory data.""" raise NotImplementedError("clear_all_memory not implemented") def _atomic_write_file(target_path: Path, content: str) -> None: dir_path = target_path.parent temp_path = dir_path / f".tmp-{os.getpid()}-{uuid.uuid4()}" data = content.encode("utf-8") try: fd = os.open(temp_path, os.O_CREAT | os.O_EXCL | os.O_WRONLY, _FILE_CREATE_MODE) try: offset = 0 while offset < len(data): written = os.write(fd, data[offset:]) if written == 0: raise OSError("os.write returned 0") offset += written os.fsync(fd) finally: os.close(fd) os.replace(temp_path, target_path) except Exception: temp_path.unlink(missing_ok=True) raise def _secure_mkdir(path: Path, mode: int = _DIR_CREATE_MODE) -> None: """Create ``path`` and any missing parents with ``mode``, regardless of umask. ``Path.mkdir(parents=True, mode=...)`` and ``os.makedirs(mode=...)`` apply the requested mode only to the final (leaf) directory; intermediate parents are created with the process umask default, which can be world-writable under a permissive umask. We create each missing component explicitly so the entire newly-created chain has restrictive permissions — closing a symlink-swap hole where an attacker with write access to a world-writable parent could replace the sandbox root and defeat path validation. """ missing: list[Path] = [] current = path while not current.exists(): missing.append(current) parent = current.parent if parent == current: # reached filesystem root break current = parent for directory in reversed(missing): try: directory.mkdir(mode=mode) except FileExistsError: # Created concurrently between our exists() check and mkdir(); skip. continue # mkdir() is subject to umask; chmod is not. Enforce the exact mode so a # restrictive umask can't strip owner bits. (We only chmod dirs we just # created and therefore own — never pre-existing dirs.) os.chmod(directory, mode) def _validate_no_symlink_escape(target_path: Path, memory_root: Path) -> None: resolved_root = memory_root.resolve() current = target_path while True: try: resolved = current.resolve() if resolved != resolved_root and not str(resolved).startswith(str(resolved_root) + os.sep): raise ToolError("Path would escape /memories directory via symlink") return except (FileNotFoundError, OSError): parent = current.parent if parent == current or current == memory_root: return current = parent def _read_file_content(full_path: Path, memory_path: str) -> str: try: return full_path.read_text(encoding="utf-8") except FileNotFoundError as err: raise ToolError( f"The file {memory_path} no longer exists (may have been deleted or renamed concurrently)." ) from err def _format_file_size(bytes_size: int) -> str: if bytes_size == 0: return "0B" k = 1024 sizes = ["B", "K", "M", "G"] i = int(bytes_size.bit_length() - 1) // 10 i = min(i, len(sizes) - 1) size = bytes_size / (k**i) if size == int(size): return f"{int(size)}{sizes[i]}" else: return f"{size:.1f}{sizes[i]}" class BetaLocalFilesystemMemoryTool(BetaAbstractMemoryTool): """File-based memory storage implementation for Claude conversations""" def __init__(self, base_path: str = "./memory"): super().__init__() self.base_path = Path(base_path) self.memory_root = self.base_path / "memories" _secure_mkdir(self.memory_root) def _validate_path(self, path: str) -> Path: """Validate and resolve memory paths""" if not path.startswith("/memories"): raise ToolError(f"Path must start with /memories, got: {path}") relative_path = path[len("/memories") :].lstrip("/") full_path = self.memory_root / relative_path if relative_path else self.memory_root resolved_path = full_path.resolve() resolved_root = self.memory_root.resolve() if resolved_path != resolved_root and not str(resolved_path).startswith(str(resolved_root) + os.sep): raise ToolError(f"Path {path} would escape /memories directory") _validate_no_symlink_escape(resolved_path, self.memory_root) return resolved_path @override def view(self, command: BetaMemoryTool20250818ViewCommand) -> str: full_path = self._validate_path(command.path) if not full_path.exists(): raise ToolError(f"The path {command.path} does not exist. Please provide a valid path.") if full_path.is_dir(): items: List[tuple[str, str]] = [] def collect_items(dir_path: Path, relative_path: str, depth: int) -> None: if depth > 2: return try: dir_contents = sorted(dir_path.iterdir(), key=lambda x: x.name) except Exception: return for item in dir_contents: if item.name.startswith("."): continue item_relative_path = f"{relative_path}/{item.name}" if relative_path else item.name try: stat = item.stat() except Exception: continue if item.is_dir(): items.append((_format_file_size(stat.st_size), f"{item_relative_path}/")) if depth < 2: collect_items(item, item_relative_path, depth + 1) elif item.is_file(): items.append((_format_file_size(stat.st_size), item_relative_path)) collect_items(full_path, "", 1) header = f"Here're the files and directories up to 2 levels deep in {command.path}, excluding hidden items:" dir_stat = full_path.stat() dir_size = _format_file_size(dir_stat.st_size) lines = [f"{dir_size}\t{command.path}"] lines.extend([f"{size}\t{command.path}/{path}" for size, path in items]) return f"{header}\n" + "\n".join(lines) elif full_path.is_file(): content = _read_file_content(full_path, command.path) lines = content.split("\n") if len(lines) > MAX_LINES: raise ToolError(f"File {command.path} exceeds maximum line limit of 999,999 lines.") display_lines = lines start_num = 1 if command.view_range and len(command.view_range) == 2: start_line = max(1, command.view_range[0]) - 1 end_line = len(lines) if command.view_range[1] == -1 else command.view_range[1] display_lines = lines[start_line:end_line] start_num = start_line + 1 numbered_lines = [ f"{str(i + start_num).rjust(LINE_NUMBER_WIDTH)}\t{line}" for i, line in enumerate(display_lines) ] return f"Here's the content of {command.path} with line numbers:\n" + "\n".join(numbered_lines) else: raise ToolError(f"Unsupported file type for {command.path}") @override def create(self, command: BetaMemoryTool20250818CreateCommand) -> str: full_path = self._validate_path(command.path) _secure_mkdir(full_path.parent) try: fd = os.open(full_path, os.O_CREAT | os.O_EXCL | os.O_WRONLY, _FILE_CREATE_MODE) try: os.write(fd, command.file_text.encode("utf-8")) os.fsync(fd) finally: os.close(fd) except FileExistsError as err: raise ToolError(f"File {command.path} already exists") from err return f"File created successfully at: {command.path}" @override def str_replace(self, command: BetaMemoryTool20250818StrReplaceCommand) -> str: full_path = self._validate_path(command.path) if not full_path.exists(): raise ToolError(f"The path {command.path} does not exist. Please provide a valid path.") if not full_path.is_file(): raise ToolError(f"The path {command.path} is not a file.") content = _read_file_content(full_path, command.path) count = content.count(command.old_str) if count == 0: raise ToolError( f"No replacement was performed, old_str `{command.old_str}` did not appear verbatim in {command.path}." ) elif count > 1: matching_lines: List[int] = [] start = 0 while True: pos = content.find(command.old_str, start) if pos == -1: break matching_lines.append(content[:pos].count("\n") + 1) start = pos + 1 raise ToolError( f"No replacement was performed. Multiple occurrences of old_str `{command.old_str}` in lines: {', '.join(map(str, matching_lines))}. Please ensure it is unique" ) pos = content.find(command.old_str) changed_line_index = content[:pos].count("\n") new_content = content.replace(command.old_str, command.new_str) _atomic_write_file(full_path, new_content) new_lines = new_content.split("\n") context_start = max(0, changed_line_index - 2) context_end = min(len(new_lines), changed_line_index + 3) snippet = [ f"{str(line_num).rjust(LINE_NUMBER_WIDTH)}\t{new_lines[line_num - 1]}" for line_num in range(context_start + 1, context_end + 1) ] return ( f"The memory file has been edited. Here is the snippet showing the change (with line numbers):\n" + "\n".join(snippet) ) @override def insert(self, command: BetaMemoryTool20250818InsertCommand) -> str: full_path = self._validate_path(command.path) if not full_path.exists(): raise ToolError(f"The path {command.path} does not exist. Please provide a valid path.") if not full_path.is_file(): raise ToolError(f"The path {command.path} is not a file.") content = _read_file_content(full_path, command.path) lines = content.splitlines() if command.insert_line < 0 or command.insert_line > len(lines): raise ToolError( f"Invalid `insert_line` parameter: {command.insert_line}. " f"It should be within the range [0, {len(lines)}]." ) lines.insert(command.insert_line, command.insert_text.rstrip("\n")) new_content = "\n".join(lines) if not new_content.endswith("\n"): new_content += "\n" _atomic_write_file(full_path, content=new_content) return f"The file {command.path} has been edited." @override def delete(self, command: BetaMemoryTool20250818DeleteCommand) -> str: full_path = self._validate_path(command.path) if command.path == "/memories": raise ToolError("Cannot delete the /memories directory itself") try: if full_path.is_file(): full_path.unlink() elif full_path.is_dir(): shutil.rmtree(full_path) else: raise ToolError(f"The path {command.path} does not exist") except FileNotFoundError as err: raise ToolError(f"The path {command.path} does not exist") from err return f"Successfully deleted {command.path}" @override def rename(self, command: BetaMemoryTool20250818RenameCommand) -> str: old_full_path = self._validate_path(command.old_path) new_full_path = self._validate_path(command.new_path) if new_full_path.exists(): raise ToolError(f"The destination {command.new_path} already exists") _secure_mkdir(new_full_path.parent) try: old_full_path.rename(new_full_path) except FileNotFoundError as err: raise ToolError(f"The path {command.old_path} does not exist") from err return f"Successfully renamed {command.old_path} to {command.new_path}" @override def clear_all_memory(self) -> str: """Override the base implementation to provide file system clearing.""" if self.memory_root.exists(): shutil.rmtree(self.memory_root) _secure_mkdir(self.memory_root) return "All memory cleared" async def _async_atomic_write_file(target_path: AsyncPath, content: str) -> None: temp_path = target_path.parent / f".tmp-{os.getpid()}-{uuid.uuid4()}" sync_target_path = Path(str(target_path)) sync_temp_path = Path(str(temp_path)) data = content.encode("utf-8") try: def write_replace_and_sync() -> None: fd = os.open(sync_temp_path, os.O_CREAT | os.O_EXCL | os.O_WRONLY, _FILE_CREATE_MODE) try: offset = 0 while offset < len(data): written = os.write(fd, data[offset:]) if written == 0: raise OSError("os.write returned 0") offset += written os.fsync(fd) finally: os.close(fd) os.replace(sync_temp_path, sync_target_path) await run_sync(write_replace_and_sync) except Exception: await temp_path.unlink(missing_ok=True) raise async def _async_validate_no_symlink_escape(target_path: AsyncPath, memory_root: AsyncPath) -> None: sync_target = Path(str(target_path)) sync_root = Path(str(memory_root)) await run_sync(_validate_no_symlink_escape, sync_target, sync_root) async def _async_secure_mkdir(path: AsyncPath, mode: int = _DIR_CREATE_MODE) -> None: await run_sync(_secure_mkdir, Path(str(path)), mode) async def _async_read_file_content(full_path: AsyncPath, memory_path: str) -> str: try: return await full_path.read_text(encoding="utf-8") except FileNotFoundError as err: raise ToolError( f"The file {memory_path} no longer exists (may have been deleted or renamed concurrently)." ) from err class BetaAsyncLocalFilesystemMemoryTool(BetaAsyncAbstractMemoryTool): """Async file-based memory storage implementation for Claude conversations""" def __init__(self, base_path: str = "./memory"): super().__init__() self.base_path = AsyncPath(base_path) self.memory_root = self.base_path / "memories" # Note: Directory creation is deferred to async methods since __init__ can't be async async def _ensure_memory_root(self) -> None: """Ensure the memory root directory exists""" await _async_secure_mkdir(self.memory_root) async def _validate_path(self, path: str) -> AsyncPath: """Validate and resolve memory paths""" if not path.startswith("/memories"): raise ToolError(f"Path must start with /memories, got: {path}") relative_path = path[len("/memories") :].lstrip("/") full_path = self.memory_root / relative_path if relative_path else self.memory_root sync_memory_root = Path(str(self.memory_root)) sync_full_path = Path(str(full_path)) resolved_path = sync_full_path.resolve() resolved_root = sync_memory_root.resolve() if resolved_path != resolved_root and not str(resolved_path).startswith(str(resolved_root) + os.sep): raise ToolError(f"Path {path} would escape /memories directory") await _async_validate_no_symlink_escape(full_path, self.memory_root) return AsyncPath(resolved_path) @override async def view(self, command: BetaMemoryTool20250818ViewCommand) -> str: await self._ensure_memory_root() full_path = await self._validate_path(command.path) if not await full_path.exists(): raise ToolError(f"The path {command.path} does not exist. Please provide a valid path.") if await full_path.is_dir(): items: List[tuple[str, str]] = [] async def collect_items(dir_path: AsyncPath, relative_path: str, depth: int) -> None: if depth > 2: return try: dir_items = [item async for item in dir_path.iterdir()] dir_contents = sorted(dir_items, key=lambda x: x.name) except Exception: return for item in dir_contents: if item.name.startswith("."): continue item_relative_path = f"{relative_path}/{item.name}" if relative_path else item.name try: sync_item = Path(str(item)) stat = await run_sync(sync_item.stat) except Exception: continue if await item.is_dir(): items.append((_format_file_size(stat.st_size), f"{item_relative_path}/")) if depth < 2: await collect_items(item, item_relative_path, depth + 1) elif await item.is_file(): items.append((_format_file_size(stat.st_size), item_relative_path)) await collect_items(full_path, "", 1) header = f"Here're the files and directories up to 2 levels deep in {command.path}, excluding hidden items:" sync_full_path = Path(str(full_path)) dir_stat = await run_sync(sync_full_path.stat) dir_size = _format_file_size(dir_stat.st_size) lines = [f"{dir_size}\t{command.path}"] lines.extend([f"{size}\t{command.path}/{path}" for size, path in items]) return f"{header}\n" + "\n".join(lines) elif await full_path.is_file(): content = await _async_read_file_content(full_path, command.path) lines = content.split("\n") if len(lines) > MAX_LINES: raise ToolError(f"File {command.path} exceeds maximum line limit of 999,999 lines.") display_lines = lines start_num = 1 if command.view_range and len(command.view_range) == 2: start_line = max(1, command.view_range[0]) - 1 end_line = len(lines) if command.view_range[1] == -1 else command.view_range[1] display_lines = lines[start_line:end_line] start_num = start_line + 1 numbered_lines = [ f"{str(i + start_num).rjust(LINE_NUMBER_WIDTH)}\t{line}" for i, line in enumerate(display_lines) ] return f"Here's the content of {command.path} with line numbers:\n" + "\n".join(numbered_lines) else: raise ToolError(f"Unsupported file type for {command.path}") @override async def create(self, command: BetaMemoryTool20250818CreateCommand) -> str: await self._ensure_memory_root() full_path = await self._validate_path(command.path) await _async_secure_mkdir(full_path.parent) try: sync_full_path = Path(str(full_path)) def create_exclusive() -> None: fd = os.open(sync_full_path, os.O_CREAT | os.O_EXCL | os.O_WRONLY, _FILE_CREATE_MODE) try: os.write(fd, command.file_text.encode("utf-8")) os.fsync(fd) finally: os.close(fd) await run_sync(create_exclusive) except FileExistsError as err: raise ToolError(f"File {command.path} already exists") from err return f"File created successfully at: {command.path}" @override async def str_replace(self, command: BetaMemoryTool20250818StrReplaceCommand) -> str: await self._ensure_memory_root() full_path = await self._validate_path(command.path) if not await full_path.exists(): raise ToolError(f"The path {command.path} does not exist. Please provide a valid path.") if not await full_path.is_file(): raise ToolError(f"The path {command.path} is not a file.") content = await _async_read_file_content(full_path, command.path) count = content.count(command.old_str) if count == 0: raise ToolError( f"No replacement was performed, old_str `{command.old_str}` did not appear verbatim in {command.path}." ) elif count > 1: matching_lines: List[int] = [] start = 0 while True: pos = content.find(command.old_str, start) if pos == -1: break matching_lines.append(content[:pos].count("\n") + 1) start = pos + 1 raise ToolError( f"No replacement was performed. Multiple occurrences of old_str `{command.old_str}` in lines: {', '.join(map(str, matching_lines))}. Please ensure it is unique" ) pos = content.find(command.old_str) changed_line_index = content[:pos].count("\n") new_content = content.replace(command.old_str, command.new_str) await _async_atomic_write_file(full_path, new_content) new_lines = new_content.split("\n") context_start = max(0, changed_line_index - 2) context_end = min(len(new_lines), changed_line_index + 3) snippet = [ f"{str(line_num).rjust(LINE_NUMBER_WIDTH)}\t{new_lines[line_num - 1]}" for line_num in range(context_start + 1, context_end + 1) ] return ( f"The memory file has been edited. Here is the snippet showing the change (with line numbers):\n" + "\n".join(snippet) ) @override async def insert(self, command: BetaMemoryTool20250818InsertCommand) -> str: await self._ensure_memory_root() full_path = await self._validate_path(command.path) if not await full_path.exists(): raise ToolError(f"The path {command.path} does not exist. Please provide a valid path.") if not await full_path.is_file(): raise ToolError(f"The path {command.path} is not a file.") content = await _async_read_file_content(full_path, command.path) lines = content.splitlines() if command.insert_line < 0 or command.insert_line > len(lines): raise ToolError( f"Invalid `insert_line` parameter: {command.insert_line}. " f"It should be within the range [0, {len(lines)}]." ) lines.insert(command.insert_line, command.insert_text.rstrip("\n")) new_content = "\n".join(lines) if not new_content.endswith("\n"): new_content += "\n" await _async_atomic_write_file(full_path, content=new_content) return f"The file {command.path} has been edited." @override async def delete(self, command: BetaMemoryTool20250818DeleteCommand) -> str: await self._ensure_memory_root() full_path = await self._validate_path(command.path) if command.path == "/memories": raise ToolError("Cannot delete the /memories directory itself") try: if await full_path.is_file(): await full_path.unlink() elif await full_path.is_dir(): await run_sync(shutil.rmtree, str(full_path)) else: raise ToolError(f"The path {command.path} does not exist") except FileNotFoundError as err: raise ToolError(f"The path {command.path} does not exist") from err return f"Successfully deleted {command.path}" @override async def rename(self, command: BetaMemoryTool20250818RenameCommand) -> str: await self._ensure_memory_root() old_full_path = await self._validate_path(command.old_path) new_full_path = await self._validate_path(command.new_path) if await new_full_path.exists(): raise ToolError(f"The destination {command.new_path} already exists") await _async_secure_mkdir(new_full_path.parent) try: await old_full_path.rename(new_full_path) except FileNotFoundError as err: raise ToolError(f"The path {command.old_path} does not exist") from err return f"Successfully renamed {command.old_path} to {command.new_path}" @override async def clear_all_memory(self) -> str: """Override the base implementation to provide file system clearing.""" if await self.memory_root.exists(): await run_sync(shutil.rmtree, str(self.memory_root)) await _async_secure_mkdir(self.memory_root) return "All memory cleared" anthropic-sdk-python-0.120.2/src/anthropic/lib/tools/_beta_compaction_control.py000066400000000000000000000045641523216435200300410ustar00rootroot00000000000000from typing import TypedDict from typing_extensions import Required DEFAULT_SUMMARY_PROMPT = """You have been working on the task described above but have not yet completed it. Write a continuation summary that will allow you (or another instance of yourself) to resume work efficiently in a future context window where the conversation history will be replaced with this summary. Your summary should be structured, concise, and actionable. Include: 1. Task Overview The user's core request and success criteria Any clarifications or constraints they specified 2. Current State What has been completed so far Files created, modified, or analyzed (with paths if relevant) Key outputs or artifacts produced 3. Important Discoveries Technical constraints or requirements uncovered Decisions made and their rationale Errors encountered and how they were resolved What approaches were tried that didn't work (and why) 4. Next Steps Specific actions needed to complete the task Any blockers or open questions to resolve Priority order if multiple steps remain 5. Context to Preserve User preferences or style requirements Domain-specific details that aren't obvious Any promises made to the user Be concise but complete—err on the side of including information that would prevent duplicate work or repeated mistakes. Write in a way that enables immediate resumption of the task. Wrap your summary in tags.""" DEFAULT_THRESHOLD = 100_000 class CompactionControl(TypedDict, total=False): """Client-side compaction control configuration. .. deprecated:: Use server-side compaction instead by passing ``edits=[{"type": "compact_20260112"}]`` in the params passed to ``tool_runner()``. See https://platform.claude.com/docs/en/build-with-claude/compaction """ context_token_threshold: int """The context token threshold at which to trigger compaction. When the cumulative token count (input + output) across all messages exceeds this threshold, the message history will be automatically summarized and compressed. Defaults to 100,000 tokens. """ model: str """ The model to use for generating the compaction summary. If not specified, defaults to the same model used for the tool runner. """ summary_prompt: str """The prompt used to instruct the model on how to generate the summary.""" enabled: Required[bool] anthropic-sdk-python-0.120.2/src/anthropic/lib/tools/_beta_functions.py000066400000000000000000000560061523216435200261530ustar00rootroot00000000000000from __future__ import annotations import sys import logging from abc import ABC, abstractmethod from typing import Any, Union, Generic, TypeVar, Callable, Iterable, Coroutine, cast, overload from inspect import isawaitable, isasyncgenfunction, iscoroutinefunction, isgeneratorfunction from collections.abc import Awaitable from typing_extensions import Literal, TypeAlias, override import anyio import pydantic import docstring_parser from pydantic import BaseModel from ... import _compat from ..._utils import is_dict from ..._compat import cached_property from ..._models import TypeAdapter from ...types.beta import BetaToolParam, BetaToolUnionParam, BetaCacheControlEphemeralParam from ..._utils._utils import CallableT from ...types.tool_param import InputSchema from ...types.beta.beta_tool_result_block_param import Content as BetaContent log = logging.getLogger(__name__) BetaFunctionToolResultType: TypeAlias = Union[str, Iterable[BetaContent]] class ToolError(Exception): """Error that can be raised from a tool to return structured content with ``is_error: True``. When the tool runner catches this error, it will use the :attr:`content` property as the tool result instead of ``repr(exc)``. Example:: raise ToolError( [ {"type": "text", "text": "Error details here"}, {"type": "image", "source": {"type": "base64", "data": "...", "media_type": "image/png"}}, ] ) """ content: BetaFunctionToolResultType def __init__(self, content: BetaFunctionToolResultType) -> None: if isinstance(content, str): message = content else: parts: list[str] = [] for block in content: text = block.get("text") if text is not None: parts.append(str(text)) else: parts.append(f"[{block.get('type', 'unknown')}]") message = " ".join(parts) if parts else "Tool error" super().__init__(message) self.content = content Function = Callable[..., BetaFunctionToolResultType] FunctionT = TypeVar("FunctionT", bound=Function) AsyncFunction = Callable[..., Coroutine[Any, Any, BetaFunctionToolResultType]] AsyncFunctionT = TypeVar("AsyncFunctionT", bound=AsyncFunction) class BetaBuiltinFunctionTool(ABC): @abstractmethod def to_dict(self) -> BetaToolUnionParam: ... @abstractmethod def call(self, input: object) -> BetaFunctionToolResultType: ... @property def name(self) -> str: raw = self.to_dict() if "mcp_server_name" in raw: return raw["mcp_server_name"] return raw["name"] class BetaAsyncBuiltinFunctionTool(ABC): @abstractmethod def to_dict(self) -> BetaToolUnionParam: ... @abstractmethod async def call(self, input: object) -> BetaFunctionToolResultType: ... @property def name(self) -> str: raw = self.to_dict() if "mcp_server_name" in raw: return raw["mcp_server_name"] return raw["name"] class BaseFunctionTool(Generic[CallableT]): func: CallableT """The function this tool is wrapping""" name: str """The name of the tool that will be sent to the API""" description: str input_schema: InputSchema close: Callable[[], None | Awaitable[None]] | None = None """Optional cleanup hook. A tool that owns a resource (a subprocess, a connection, …) may set this on its instance; the result is awaited if it returns an awaitable. Which runners actually invoke it differs — check before relying on it for a stateful tool: - ``SessionToolRunner`` (``client.beta.sessions.events.tool_runner(...)``) and the :class:`~anthropic.lib.environments.EnvironmentWorker` built on it **do** call ``close`` when the run ends. - The Messages :class:`BetaToolRunner` / ``BetaAsyncToolRunner`` (``client.beta.messages.tool_runner(...)``) does **not** call ``close``. A stateful tool (e.g. the ``bash`` tool's subprocess) handed to the Messages tool runner therefore leaks its resource — run it under ``SessionToolRunner`` / the environment worker instead. """ _context_manager: object | None = None """Set by :func:`beta_tool` / :func:`beta_async_tool` when the tool was defined as a (sync/async) context manager: the *entered* context manager whose ``__exit__`` / ``__aexit__`` the tool-runner cleanup path drives on the way out. Additive to :attr:`close` — both run if both are present, so other tool-runner consumers that only set ``close`` keep working unchanged. """ def __init__( self, func: CallableT, *, name: str | None = None, description: str | None = None, input_schema: InputSchema | type[BaseModel] | None = None, defer_loading: bool | None = None, cache_control: BetaCacheControlEphemeralParam | None = None, allowed_callers: list[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] | None = None, eager_input_streaming: bool | None = None, input_examples: Iterable[dict[str, object]] | None = None, strict: bool | None = None, ) -> None: if _compat.PYDANTIC_V1: raise RuntimeError("Tool functions are only supported with Pydantic v2") self.func = func self._func_with_validate = pydantic.validate_call(func) self.name = name or func.__name__ self._defer_loading = defer_loading self._cache_control = cache_control self._allowed_callers = allowed_callers self._eager_input_streaming = eager_input_streaming self._input_examples = input_examples self._strict = strict self.description = description or self._get_description_from_docstring() if input_schema is not None: if isinstance(input_schema, type): self.input_schema: InputSchema = input_schema.model_json_schema() else: self.input_schema = input_schema else: self.input_schema = self._create_schema_from_function() @property def __call__(self) -> CallableT: return self.func def to_dict(self) -> BetaToolParam: defn: BetaToolParam = { "name": self.name, "description": self.description, "input_schema": self.input_schema, } if self._defer_loading is not None: defn["defer_loading"] = self._defer_loading if self._cache_control is not None: defn["cache_control"] = self._cache_control if self._allowed_callers is not None: defn["allowed_callers"] = self._allowed_callers if self._eager_input_streaming is not None: defn["eager_input_streaming"] = self._eager_input_streaming if self._input_examples is not None: defn["input_examples"] = self._input_examples if self._strict is not None: defn["strict"] = self._strict return defn @cached_property def _parsed_docstring(self) -> docstring_parser.Docstring: return docstring_parser.parse(self.func.__doc__ or "") def _get_description_from_docstring(self) -> str: """Extract description from parsed docstring.""" if self._parsed_docstring.short_description: description = self._parsed_docstring.short_description if self._parsed_docstring.long_description: description += f"\n\n{self._parsed_docstring.long_description}" return description return "" def _create_schema_from_function(self) -> InputSchema: """Create JSON schema from function signature using pydantic.""" from pydantic_core import CoreSchema from pydantic.json_schema import JsonSchemaValue, GenerateJsonSchema from pydantic_core.core_schema import ArgumentsParameter class CustomGenerateJsonSchema(GenerateJsonSchema): def __init__(self, *, func: Callable[..., Any], parsed_docstring: Any) -> None: super().__init__() self._func = func self._parsed_docstring = parsed_docstring def __call__(self, *_args: Any, **_kwds: Any) -> "CustomGenerateJsonSchema": # noqa: ARG002 return self @override def kw_arguments_schema( self, arguments: "list[ArgumentsParameter]", var_kwargs_schema: CoreSchema | None, ) -> JsonSchemaValue: schema = super().kw_arguments_schema(arguments, var_kwargs_schema) if schema.get("type") != "object": return schema properties = schema.get("properties") if not properties or not is_dict(properties): return schema # Add parameter descriptions from docstring for param in self._parsed_docstring.params: prop_schema = properties.get(param.arg_name) if not prop_schema or not is_dict(prop_schema): continue if param.description and "description" not in prop_schema: prop_schema["description"] = param.description return schema schema_generator = CustomGenerateJsonSchema(func=self.func, parsed_docstring=self._parsed_docstring) return self._adapter.json_schema(schema_generator=schema_generator) # type: ignore @cached_property def _adapter(self) -> TypeAdapter[Any]: return TypeAdapter(self._func_with_validate) class BetaFunctionTool(BaseFunctionTool[FunctionT]): def call(self, input: object) -> BetaFunctionToolResultType: if iscoroutinefunction(self.func): raise RuntimeError("Cannot call a coroutine function synchronously. Use `@async_tool` instead.") if not is_dict(input): raise TypeError(f"Input must be a dictionary, got {type(input).__name__}") try: return self._func_with_validate(**cast(Any, input)) except pydantic.ValidationError as e: raise ValueError(f"Invalid arguments for function {self.name}") from e class BetaAsyncFunctionTool(BaseFunctionTool[AsyncFunctionT]): async def call(self, input: object) -> BetaFunctionToolResultType: if not iscoroutinefunction(self.func): raise RuntimeError("Cannot call a synchronous function asynchronously. Use `@tool` instead.") if not is_dict(input): raise TypeError(f"Input must be a dictionary, got {type(input).__name__}") try: return await self._func_with_validate(**cast(Any, input)) except pydantic.ValidationError as e: raise ValueError(f"Invalid arguments for function {self.name}") from e def _is_sync_cm_factory(fn: object) -> bool: """True when ``fn`` is a function produced by :func:`contextlib.contextmanager`. ``contextmanager`` wraps the generator function with ``functools.wraps``, so the original generator function is reachable as ``__wrapped__`` — the same signal :mod:`inspect` itself uses. We never call ``fn`` to find out, so a plain tool function is never accidentally invoked during detection. """ wrapped = getattr(fn, "__wrapped__", None) return wrapped is not None and isgeneratorfunction(wrapped) def _is_async_cm_factory(fn: object) -> bool: """True when ``fn`` is a function produced by :func:`contextlib.asynccontextmanager`.""" wrapped = getattr(fn, "__wrapped__", None) return wrapped is not None and isasyncgenfunction(wrapped) async def aclose_runnable_tool(tool: object) -> None: """Run a runnable tool's optional cleanup. Drives the legacy ``aclose`` / ``close`` attribute (awaited if it returns an awaitable) and, when the tool was defined as a context manager via :func:`beta_tool` / :func:`beta_async_tool`, its ``__exit__`` / ``__aexit__``. Both run when both are present — the context-manager support is purely additive to ``close``. Exceptions are logged, never raised, so one tool's bad cleanup can't abort another tool's. """ closer = getattr(tool, "aclose", None) or getattr(tool, "close", None) if closer is not None: try: result = closer() if isawaitable(result): await result except Exception as e: log.warning("tool.close failed tool=%s error=%s", getattr(tool, "name", "?"), e) cm = getattr(tool, "_context_manager", None) if cm is not None: try: aexit = getattr(cm, "__aexit__", None) if aexit is not None: await aexit(None, None, None) else: cm.__exit__(None, None, None) except Exception as e: log.warning("tool context-manager cleanup failed tool=%s error=%s", getattr(tool, "name", "?"), e) @overload def beta_tool(func: FunctionT) -> BetaFunctionTool[FunctionT]: ... @overload def beta_tool( func: FunctionT, *, name: str | None = None, description: str | None = None, input_schema: InputSchema | type[BaseModel] | None = None, defer_loading: bool | None = None, cache_control: BetaCacheControlEphemeralParam | None = None, allowed_callers: list[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] | None = None, eager_input_streaming: bool | None = None, input_examples: Iterable[dict[str, object]] | None = None, strict: bool | None = None, ) -> BetaFunctionTool[FunctionT]: ... @overload def beta_tool( *, name: str | None = None, description: str | None = None, input_schema: InputSchema | type[BaseModel] | None = None, defer_loading: bool | None = None, cache_control: BetaCacheControlEphemeralParam | None = None, allowed_callers: list[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] | None = None, eager_input_streaming: bool | None = None, input_examples: Iterable[dict[str, object]] | None = None, strict: bool | None = None, ) -> Callable[[FunctionT], BetaFunctionTool[FunctionT]]: ... def beta_tool( func: FunctionT | None = None, *, name: str | None = None, description: str | None = None, input_schema: InputSchema | type[BaseModel] | None = None, defer_loading: bool | None = None, cache_control: BetaCacheControlEphemeralParam | None = None, allowed_callers: list[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] | None = None, eager_input_streaming: bool | None = None, input_examples: Iterable[dict[str, object]] | None = None, strict: bool | None = None, ) -> BetaFunctionTool[FunctionT] | Callable[[FunctionT], BetaFunctionTool[FunctionT]]: """Create a FunctionTool from a function with automatic schema inference. Can be used as a decorator with or without parentheses: @function_tool def my_func(x: int) -> str: ... @function_tool() def my_func(x: int) -> str: ... @function_tool(name="custom_name") def my_func(x: int) -> str: ... """ if _compat.PYDANTIC_V1: raise RuntimeError("Tool functions are only supported with Pydantic v2") def _make(fn: FunctionT) -> BetaFunctionTool[FunctionT]: if _is_async_cm_factory(fn): raise TypeError( "@beta_tool was applied to an @asynccontextmanager; " "use @beta_async_tool for an async context-manager tool" ) if _is_sync_cm_factory(fn): # The decorated function is a @contextmanager that yields the tool # callable: enter it now to obtain the callable, build the tool from # it (so schema inference still sees the real signature), and keep # the entered context manager so the runner cleanup can exit it. cm = cast(Any, fn)() inner = cm.__enter__() try: tool = BetaFunctionTool( cast(FunctionT, inner), name=name, description=description, input_schema=input_schema, defer_loading=defer_loading, cache_control=cache_control, allowed_callers=allowed_callers, eager_input_streaming=eager_input_streaming, input_examples=input_examples, strict=strict, ) except BaseException: # Construction failed after we entered the context manager — # unwind it so its resource isn't leaked. cm.__exit__(*sys.exc_info()) raise tool._context_manager = cm return tool return BetaFunctionTool( fn, name=name, description=description, input_schema=input_schema, defer_loading=defer_loading, cache_control=cache_control, allowed_callers=allowed_callers, eager_input_streaming=eager_input_streaming, input_examples=input_examples, strict=strict, ) if func is not None: return _make(func) return _make @overload def beta_async_tool(func: AsyncFunctionT) -> BetaAsyncFunctionTool[AsyncFunctionT]: ... @overload def beta_async_tool( func: AsyncFunctionT, *, name: str | None = None, description: str | None = None, input_schema: InputSchema | type[BaseModel] | None = None, defer_loading: bool | None = None, cache_control: BetaCacheControlEphemeralParam | None = None, allowed_callers: list[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] | None = None, eager_input_streaming: bool | None = None, input_examples: Iterable[dict[str, object]] | None = None, strict: bool | None = None, ) -> BetaAsyncFunctionTool[AsyncFunctionT]: ... # noqa: E501 @overload def beta_async_tool( *, name: str | None = None, description: str | None = None, input_schema: InputSchema | type[BaseModel] | None = None, defer_loading: bool | None = None, cache_control: BetaCacheControlEphemeralParam | None = None, allowed_callers: list[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] | None = None, eager_input_streaming: bool | None = None, input_examples: Iterable[dict[str, object]] | None = None, strict: bool | None = None, ) -> Callable[[AsyncFunctionT], BetaAsyncFunctionTool[AsyncFunctionT]]: ... def beta_async_tool( func: AsyncFunctionT | None = None, *, name: str | None = None, description: str | None = None, input_schema: InputSchema | type[BaseModel] | None = None, defer_loading: bool | None = None, cache_control: BetaCacheControlEphemeralParam | None = None, allowed_callers: list[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] | None = None, eager_input_streaming: bool | None = None, input_examples: Iterable[dict[str, object]] | None = None, strict: bool | None = None, ) -> BetaAsyncFunctionTool[AsyncFunctionT] | Callable[[AsyncFunctionT], BetaAsyncFunctionTool[AsyncFunctionT]]: """Create an AsyncFunctionTool from a function with automatic schema inference. Can be used as a decorator with or without parentheses: @async_tool async def my_func(x: int) -> str: ... @async_tool() async def my_func(x: int) -> str: ... @async_tool(name="custom_name") async def my_func(x: int) -> str: ... """ if _compat.PYDANTIC_V1: raise RuntimeError("Tool functions are only supported with Pydantic v2") def _make(fn: AsyncFunctionT) -> BetaAsyncFunctionTool[AsyncFunctionT]: if _is_sync_cm_factory(fn): raise TypeError( "@beta_async_tool was applied to a @contextmanager; use @beta_tool for a sync context-manager tool" ) if _is_async_cm_factory(fn): # The decorated function is an @asynccontextmanager that yields the # tool callable. Entering it requires awaiting, which the decorator # can't do, so enter lazily on first call and cache the result; the # parameters can't be inferred until then, so an explicit # ``input_schema`` is required. if input_schema is None: raise TypeError( "an @asynccontextmanager tool needs an explicit input_schema= " "(its parameters can't be inferred until the context manager is entered)" ) cm = cast(Any, fn)() state: dict[str, Any] = {"validated": None, "entered": False} enter_lock = anyio.Lock() tool_box: list[BetaAsyncFunctionTool[AsyncFunctionT]] = [] async def _entered() -> Any: if not state["entered"]: async with enter_lock: if not state["entered"]: inner = await cm.__aenter__() state["validated"] = pydantic.validate_call(inner) state["entered"] = True # Only now is there an entered __aexit__ to drive on # the cleanup path. tool_box[0]._context_manager = cm return state["validated"] async def _lazy(**kwargs: Any) -> BetaFunctionToolResultType: validated = await _entered() return cast(BetaFunctionToolResultType, await validated(**kwargs)) _lazy.__name__ = name or getattr(fn, "__name__", "tool") _lazy.__doc__ = description if description is not None else getattr(fn, "__doc__", None) tool = BetaAsyncFunctionTool( cast(AsyncFunctionT, _lazy), name=name, description=description, input_schema=input_schema, defer_loading=defer_loading, cache_control=cache_control, allowed_callers=allowed_callers, eager_input_streaming=eager_input_streaming, input_examples=input_examples, strict=strict, ) tool_box.append(tool) return tool return BetaAsyncFunctionTool( fn, name=name, description=description, input_schema=input_schema, defer_loading=defer_loading, cache_control=cache_control, allowed_callers=allowed_callers, eager_input_streaming=eager_input_streaming, input_examples=input_examples, strict=strict, ) if func is not None: return _make(func) return _make BetaRunnableTool = Union[BetaFunctionTool[Any], BetaBuiltinFunctionTool] BetaAsyncRunnableTool = Union[BetaAsyncFunctionTool[Any], BetaAsyncBuiltinFunctionTool] anthropic-sdk-python-0.120.2/src/anthropic/lib/tools/_beta_runner.py000066400000000000000000000670651523216435200254630ustar00rootroot00000000000000from __future__ import annotations import logging import warnings from abc import ABC, abstractmethod from typing import ( TYPE_CHECKING, Any, List, Union, Generic, TypeVar, Callable, Iterable, Iterator, Coroutine, AsyncIterator, ) from contextlib import contextmanager, asynccontextmanager from typing_extensions import TypedDict, override import httpx from ..._types import Body, Query, Headers, NotGiven from ..._utils import consume_sync_iterator, consume_async_iterator from ...types.beta import BetaMessage, BetaMessageParam from ..._base_client import merge_headers from ._tool_dispatch import tool_registry, tool_error_content, available_tool_names from ._beta_functions import ( ToolError, BetaFunctionTool, BetaRunnableTool, BetaAsyncFunctionTool, BetaAsyncRunnableTool, BetaBuiltinFunctionTool, BetaAsyncBuiltinFunctionTool, ) from .._stainless_helpers import helper_header, stainless_helper_header from ._beta_compaction_control import DEFAULT_THRESHOLD, DEFAULT_SUMMARY_PROMPT, CompactionControl from ..streaming._beta_messages import BetaMessageStream, BetaAsyncMessageStream from ...types.beta.parsed_beta_message import ResponseFormatT, ParsedBetaMessage, ParsedBetaContentBlock from ...types.beta.message_create_params import ParseMessageCreateParamsBase from ...types.beta.beta_tool_result_block_param import BetaToolResultBlockParam if TYPE_CHECKING: from ..._client import Anthropic, AsyncAnthropic AnyFunctionToolT = TypeVar( "AnyFunctionToolT", bound=Union[ BetaFunctionTool[Any], BetaAsyncFunctionTool[Any], BetaBuiltinFunctionTool, BetaAsyncBuiltinFunctionTool ], ) RunnerItemT = TypeVar("RunnerItemT") log = logging.getLogger(__name__) class RequestOptions(TypedDict, total=False): extra_headers: Headers | None extra_query: Query | None extra_body: Body | None timeout: float | httpx.Timeout | None | NotGiven class BaseToolRunner(Generic[AnyFunctionToolT, ResponseFormatT]): def __init__( self, *, params: ParseMessageCreateParamsBase[ResponseFormatT], options: RequestOptions, tools: Iterable[AnyFunctionToolT], max_iterations: int | None = None, compaction_control: CompactionControl | None = None, ) -> None: self._tools_by_name = tool_registry(tools) self._params: ParseMessageCreateParamsBase[ResponseFormatT] = { **params, "messages": [message for message in params["messages"]], } helper_header = stainless_helper_header( tools=self._tools_by_name.values(), messages=params.get("messages"), ) if helper_header: merged_headers = merge_headers(helper_header, options.get("extra_headers") or {}) options = {**options, "extra_headers": merged_headers} self._options = options self._messages_modified = False self._cached_tool_call_response: BetaMessageParam | None = None self._max_iterations = max_iterations self._iteration_count = 0 self._compaction_control = compaction_control def set_messages_params( self, params: ParseMessageCreateParamsBase[ResponseFormatT] | Callable[[ParseMessageCreateParamsBase[ResponseFormatT]], ParseMessageCreateParamsBase[ResponseFormatT]], ) -> None: """ Update the parameters for the next API call. This invalidates any cached tool responses. Args: params (ParsedMessageCreateParamsBase[ResponseFormatT] | Callable): Either new parameters or a function to mutate existing parameters """ if callable(params): params = params(self._params) self._params = params def append_messages(self, *messages: BetaMessageParam | ParsedBetaMessage[ResponseFormatT]) -> None: """Add one or more messages to the conversation history. This invalidates the cached tool response, i.e. if tools were already called, then they will be called again on the next loop iteration. """ message_params: List[BetaMessageParam] = [ {"role": message.role, "content": message.content} if isinstance(message, BetaMessage) else message for message in messages ] self._messages_modified = True self.set_messages_params(lambda params: {**params, "messages": [*params["messages"], *message_params]}) self._cached_tool_call_response = None def _should_stop(self) -> bool: if self._max_iterations is not None and self._iteration_count >= self._max_iterations: return True return False def _available_tool_names(self) -> set[str]: """The tool names currently available, after applying any mid-conversation ``tool_removal`` / ``tool_addition`` blocks. Removal is only a hint to the model, which can still emit a ``tool_use`` for a withdrawn tool; a name absent from this set routes that call down the same unknown-tool path as a tool that was never declared. """ return available_tool_names(self._params["messages"], self._tools_by_name) class BaseSyncToolRunner(BaseToolRunner[BetaRunnableTool, ResponseFormatT], Generic[RunnerItemT, ResponseFormatT], ABC): def __init__( self, *, params: ParseMessageCreateParamsBase[ResponseFormatT], options: RequestOptions, tools: Iterable[BetaRunnableTool], client: Anthropic, max_iterations: int | None = None, compaction_control: CompactionControl | None = None, ) -> None: super().__init__( params=params, options=options, tools=tools, max_iterations=max_iterations, compaction_control=compaction_control, ) self._client = client if compaction_control is not None and compaction_control.get("enabled"): warnings.warn( "The 'compaction_control' parameter is deprecated and will be removed in a future version. " "Use server-side compaction instead by passing `edits=[{'type': 'compact_20260112'}]` in your " "the params passed to `tool_runner()`. See https://platform.claude.com/docs/en/build-with-claude/compaction", DeprecationWarning, stacklevel=3, ) self._iterator = self.__run__() self._last_message: ( Callable[[], ParsedBetaMessage[ResponseFormatT]] | ParsedBetaMessage[ResponseFormatT] | None ) = None def __next__(self) -> RunnerItemT: return self._iterator.__next__() def __iter__(self) -> Iterator[RunnerItemT]: for item in self._iterator: yield item @abstractmethod @contextmanager def _handle_request(self) -> Iterator[RunnerItemT]: raise NotImplementedError() yield # type: ignore[unreachable] def _check_and_compact(self) -> bool: """ Check token usage and compact messages if threshold exceeded. Returns True if compaction was performed, False otherwise. """ if self._compaction_control is None or not self._compaction_control["enabled"]: return False message = self._get_last_message() tokens_used = 0 if message is not None: total_input_tokens = ( message.usage.input_tokens + (message.usage.cache_creation_input_tokens or 0) + (message.usage.cache_read_input_tokens or 0) ) tokens_used = total_input_tokens + message.usage.output_tokens threshold = self._compaction_control.get("context_token_threshold", DEFAULT_THRESHOLD) if tokens_used < threshold: return False # Perform compaction log.info(f"Token usage {tokens_used} has exceeded the threshold of {threshold}. Performing compaction.") model = self._compaction_control.get("model", self._params["model"]) messages = list(self._params["messages"]) if messages[-1]["role"] == "assistant": # Remove tool_use blocks from the last message to avoid 400 error # (tool_use requires tool_result, which we don't have yet) non_tool_blocks = [ block for block in messages[-1]["content"] if isinstance(block, dict) and block.get("type") != "tool_use" ] if non_tool_blocks: messages[-1]["content"] = non_tool_blocks else: messages.pop() messages = [ *messages, BetaMessageParam( role="user", content=self._compaction_control.get("summary_prompt", DEFAULT_SUMMARY_PROMPT), ), ] response = self._client.beta.messages.create( model=model, messages=messages, max_tokens=self._params["max_tokens"], extra_headers=helper_header("compaction"), ) log.info(f"Compaction complete. New token usage: {response.usage.output_tokens}") first_content = list(response.content)[0] if first_content.type != "text": raise ValueError("Compaction response content is not of type 'text'") self.set_messages_params( lambda params: { **params, "messages": [ { "role": "user", "content": [ { "type": "text", "text": first_content.text, } ], } ], } ) return True def __run__(self) -> Iterator[RunnerItemT]: while not self._should_stop(): with self._handle_request() as item: yield item message = self._get_last_message() assert message is not None # Update container from response for programmatic tool calling support last_assistant_message = self._get_last_assistant_message() if last_assistant_message is not None and last_assistant_message.container is not None: self._params["container"] = last_assistant_message.container.id self._iteration_count += 1 # Refusal-terminated turns are terminal: executing their tool_use blocks would # fire side effects the model never confirmed, and the resulting tool_results # cannot be replayed coherently. Surface the refusal as the final message. if message.stop_reason == "refusal": log.debug("Turn ended with a refusal, exiting from tool runner loop.") return # If the compaction was performed, skip tool call generation this iteration if not self._check_and_compact(): response = self.generate_tool_call_response() if response is None: log.debug("Tool call was not requested, exiting from tool runner loop.") return if not self._messages_modified: self.append_messages(message, response) self._messages_modified = False self._cached_tool_call_response = None def until_done(self) -> ParsedBetaMessage[ResponseFormatT]: """ Consumes the tool runner stream and returns the last message if it has not been consumed yet. If it has, it simply returns the last message. """ consume_sync_iterator(self) last_message = self._get_last_message() assert last_message is not None return last_message def generate_tool_call_response(self) -> BetaMessageParam | None: """Generate a MessageParam by calling tool functions with any tool use blocks from the last message. Note the tool call response is cached, repeated calls to this method will return the same response. None can be returned if no tool call was applicable. """ if self._cached_tool_call_response is not None: log.debug("Returning cached tool call response.") return self._cached_tool_call_response response = self._generate_tool_call_response() self._cached_tool_call_response = response return response def _generate_tool_call_response(self) -> BetaMessageParam | None: content = self._get_last_assistant_message_content() if not content: return None tool_use_blocks = [block for block in content if block.type == "tool_use"] if not tool_use_blocks: return None results: list[BetaToolResultBlockParam] = [] available = self._available_tool_names() for tool_use in tool_use_blocks: tool = self._tools_by_name.get(tool_use.name) if tool_use.name in available else None if tool is None: warnings.warn( f"Tool '{tool_use.name}' not found in tool runner. " f"Available tools: {list(self._tools_by_name.keys())}. " f"If using a raw tool definition, handle the tool call manually and use `append_messages()` to add the result. " f"Otherwise, pass the tool using `beta_tool(func)` or a `@beta_tool` decorated function.", UserWarning, stacklevel=3, ) results.append( { "type": "tool_result", "tool_use_id": tool_use.id, "content": f"Error: Tool '{tool_use.name}' not found", "is_error": True, } ) continue try: result = tool.call(tool_use.input) results.append({"type": "tool_result", "tool_use_id": tool_use.id, "content": result}) except ToolError as exc: results.append( { "type": "tool_result", "tool_use_id": tool_use.id, "content": tool_error_content(exc), "is_error": True, } ) except Exception as exc: log.exception(f"Error occurred while calling tool: {tool.name}", exc_info=exc) results.append( { "type": "tool_result", "tool_use_id": tool_use.id, "content": tool_error_content(exc), "is_error": True, } ) return {"role": "user", "content": results} def _get_last_message(self) -> ParsedBetaMessage[ResponseFormatT] | None: if callable(self._last_message): return self._last_message() return self._last_message def _get_last_assistant_message(self) -> ParsedBetaMessage[ResponseFormatT] | None: last_message = self._get_last_message() if last_message is None or last_message.role != "assistant" or not last_message.content: return None return last_message def _get_last_assistant_message_content(self) -> list[ParsedBetaContentBlock[ResponseFormatT]] | None: last_assistant_message = self._get_last_assistant_message() if last_assistant_message is None: return None return last_assistant_message.content class BetaToolRunner(BaseSyncToolRunner[ParsedBetaMessage[ResponseFormatT], ResponseFormatT]): @override @contextmanager def _handle_request(self) -> Iterator[ParsedBetaMessage[ResponseFormatT]]: message = self._client.beta.messages.parse(**self._params, **self._options) self._last_message = message yield message class BetaStreamingToolRunner(BaseSyncToolRunner[BetaMessageStream[ResponseFormatT], ResponseFormatT]): @override @contextmanager def _handle_request(self) -> Iterator[BetaMessageStream[ResponseFormatT]]: with self._client.beta.messages.stream(**self._params, **self._options) as stream: self._last_message = stream.get_final_message yield stream class BaseAsyncToolRunner( BaseToolRunner[BetaAsyncRunnableTool, ResponseFormatT], Generic[RunnerItemT, ResponseFormatT], ABC ): def __init__( self, *, params: ParseMessageCreateParamsBase[ResponseFormatT], options: RequestOptions, tools: Iterable[BetaAsyncRunnableTool], client: AsyncAnthropic, max_iterations: int | None = None, compaction_control: CompactionControl | None = None, ) -> None: super().__init__( params=params, options=options, tools=tools, max_iterations=max_iterations, compaction_control=compaction_control, ) self._client = client if compaction_control is not None and compaction_control.get("enabled"): warnings.warn( "The 'compaction_control' parameter is deprecated and will be removed in a future version. " "Use server-side compaction instead by passing `edits=[{'type': 'compact_20260112'}]` in your " "the params passed to `tool_runner()`. See https://platform.claude.com/docs/en/build-with-claude/compaction", DeprecationWarning, stacklevel=3, ) self._iterator = self.__run__() self._last_message: ( Callable[[], Coroutine[None, None, ParsedBetaMessage[ResponseFormatT]]] | ParsedBetaMessage[ResponseFormatT] | None ) = None async def __anext__(self) -> RunnerItemT: return await self._iterator.__anext__() async def __aiter__(self) -> AsyncIterator[RunnerItemT]: async for item in self._iterator: yield item @abstractmethod @asynccontextmanager async def _handle_request(self) -> AsyncIterator[RunnerItemT]: raise NotImplementedError() yield # type: ignore[unreachable] async def _check_and_compact(self) -> bool: """ Check token usage and compact messages if threshold exceeded. Returns True if compaction was performed, False otherwise. """ if self._compaction_control is None or not self._compaction_control["enabled"]: return False message = await self._get_last_message() tokens_used = 0 if message is not None: total_input_tokens = ( message.usage.input_tokens + (message.usage.cache_creation_input_tokens or 0) + (message.usage.cache_read_input_tokens or 0) ) tokens_used = total_input_tokens + message.usage.output_tokens threshold = self._compaction_control.get("context_token_threshold", DEFAULT_THRESHOLD) if tokens_used < threshold: return False # Perform compaction log.info(f"Token usage {tokens_used} has exceeded the threshold of {threshold}. Performing compaction.") model = self._compaction_control.get("model", self._params["model"]) messages = list(self._params["messages"]) if messages[-1]["role"] == "assistant": # Remove tool_use blocks from the last message to avoid 400 error # (tool_use requires tool_result, which we don't have yet) non_tool_blocks = [ block for block in messages[-1]["content"] if isinstance(block, dict) and block.get("type") != "tool_use" ] if non_tool_blocks: messages[-1]["content"] = non_tool_blocks else: messages.pop() messages = [ *messages, BetaMessageParam( role="user", content=self._compaction_control.get("summary_prompt", DEFAULT_SUMMARY_PROMPT), ), ] response = await self._client.beta.messages.create( model=model, messages=messages, max_tokens=self._params["max_tokens"], extra_headers=helper_header("compaction"), ) log.info(f"Compaction complete. New token usage: {response.usage.output_tokens}") first_content = list(response.content)[0] if first_content.type != "text": raise ValueError("Compaction response content is not of type 'text'") self.set_messages_params( lambda params: { **params, "messages": [ { "role": "user", "content": [ { "type": "text", "text": first_content.text, } ], } ], } ) return True async def __run__(self) -> AsyncIterator[RunnerItemT]: while not self._should_stop(): async with self._handle_request() as item: yield item message = await self._get_last_message() assert message is not None # Update container from response for programmatic tool calling support last_assistant_message = await self._get_last_assistant_message() if last_assistant_message is not None and last_assistant_message.container is not None: self._params["container"] = last_assistant_message.container.id self._iteration_count += 1 # Refusal-terminated turns are terminal: executing their tool_use blocks would # fire side effects the model never confirmed, and the resulting tool_results # cannot be replayed coherently. Surface the refusal as the final message. if message.stop_reason == "refusal": log.debug("Turn ended with a refusal, exiting from tool runner loop.") return # If the compaction was performed, skip tool call generation this iteration if not await self._check_and_compact(): response = await self.generate_tool_call_response() if response is None: log.debug("Tool call was not requested, exiting from tool runner loop.") return if not self._messages_modified: self.append_messages(message, response) self._messages_modified = False self._cached_tool_call_response = None async def until_done(self) -> ParsedBetaMessage[ResponseFormatT]: """ Consumes the tool runner stream and returns the last message if it has not been consumed yet. If it has, it simply returns the last message. """ await consume_async_iterator(self) last_message = await self._get_last_message() assert last_message is not None return last_message async def generate_tool_call_response(self) -> BetaMessageParam | None: """Generate a MessageParam by calling tool functions with any tool use blocks from the last message. Note the tool call response is cached, repeated calls to this method will return the same response. None can be returned if no tool call was applicable. """ if self._cached_tool_call_response is not None: log.debug("Returning cached tool call response.") return self._cached_tool_call_response response = await self._generate_tool_call_response() self._cached_tool_call_response = response return response async def _get_last_message(self) -> ParsedBetaMessage[ResponseFormatT] | None: if callable(self._last_message): return await self._last_message() return self._last_message async def _get_last_assistant_message(self) -> ParsedBetaMessage[ResponseFormatT] | None: last_message = await self._get_last_message() if last_message is None or last_message.role != "assistant" or not last_message.content: return None return last_message async def _get_last_assistant_message_content(self) -> list[ParsedBetaContentBlock[ResponseFormatT]] | None: last_assistant_message = await self._get_last_assistant_message() if last_assistant_message is None: return None return last_assistant_message.content async def _generate_tool_call_response(self) -> BetaMessageParam | None: content = await self._get_last_assistant_message_content() if not content: return None tool_use_blocks = [block for block in content if block.type == "tool_use"] if not tool_use_blocks: return None results: list[BetaToolResultBlockParam] = [] available = self._available_tool_names() for tool_use in tool_use_blocks: tool = self._tools_by_name.get(tool_use.name) if tool_use.name in available else None if tool is None: warnings.warn( f"Tool '{tool_use.name}' not found in tool runner. " f"Available tools: {list(self._tools_by_name.keys())}. " f"If using a raw tool definition, handle the tool call manually and use `append_messages()` to add the result. " f"Otherwise, pass the tool using `beta_async_tool(func)` or a `@beta_async_tool` decorated function.", UserWarning, stacklevel=3, ) results.append( { "type": "tool_result", "tool_use_id": tool_use.id, "content": f"Error: Tool '{tool_use.name}' not found", "is_error": True, } ) continue try: result = await tool.call(tool_use.input) results.append({"type": "tool_result", "tool_use_id": tool_use.id, "content": result}) except ToolError as exc: results.append( { "type": "tool_result", "tool_use_id": tool_use.id, "content": tool_error_content(exc), "is_error": True, } ) except Exception as exc: log.exception(f"Error occurred while calling tool: {tool.name}", exc_info=exc) results.append( { "type": "tool_result", "tool_use_id": tool_use.id, "content": tool_error_content(exc), "is_error": True, } ) return {"role": "user", "content": results} class BetaAsyncToolRunner(BaseAsyncToolRunner[ParsedBetaMessage[ResponseFormatT], ResponseFormatT]): @override @asynccontextmanager async def _handle_request(self) -> AsyncIterator[ParsedBetaMessage[ResponseFormatT]]: message = await self._client.beta.messages.parse(**self._params, **self._options) self._last_message = message yield message class BetaAsyncStreamingToolRunner(BaseAsyncToolRunner[BetaAsyncMessageStream[ResponseFormatT], ResponseFormatT]): @override @asynccontextmanager async def _handle_request(self) -> AsyncIterator[BetaAsyncMessageStream[ResponseFormatT]]: async with self._client.beta.messages.stream(**self._params, **self._options) as stream: self._last_message = stream.get_final_message yield stream anthropic-sdk-python-0.120.2/src/anthropic/lib/tools/_beta_session_runner.py000066400000000000000000001373271523216435200272250ustar00rootroot00000000000000"""The sessions-side tool runner — the managed-agents counterpart to ``client.beta.messages.tool_runner``. :class:`SessionToolRunner` attaches to a managed-agents session's event stream, reconciles against the events-list endpoint, dispatches every ``agent.tool_use`` *and* ``agent.custom_tool_use`` event against a local tool registry, posts the matching result event back (``user.tool_result`` / ``user.custom_tool_result``), and yields one :class:`DispatchedToolCall` per completed call. A call the server gated behind user confirmation (``evaluated_permission`` ``ask``, e.g. an ``always_ask`` tool) is held until its ``user.tool_confirmation`` event arrives — executed on ``allow``, never executed on ``deny``. It also stops itself once the session has been idle (``stop_reason`` ``end_turn``) for ``max_idle`` seconds. It does **not** touch the work-item lease — wrap it in :class:`anthropic.lib.environments.EnvironmentWorker` if you need heartbeating / force-stop. """ from __future__ import annotations import json import math import time import logging import contextlib from typing import TYPE_CHECKING, Union, Literal, cast from dataclasses import dataclass from collections.abc import Sequence, AsyncIterator import anyio from .._retry import TRANSIENT_ERRORS, is_fatal_status_error from ..._types import Headers from ._tool_dispatch import tool_registry, run_runnable_tool, tool_error_content from .._scoped_client import _copy_client_with_bearer_auth from ._beta_functions import ( ToolError, BetaRunnableTool, BetaAsyncRunnableTool, BetaFunctionToolResultType, aclose_runnable_tool, ) from .._stainless_helpers import helper_header from ...types.beta.sessions import ( BetaManagedAgentsAgentToolUseEvent, BetaManagedAgentsAgentCustomToolUseEvent, BetaManagedAgentsUserToolConfirmationEvent, ) from ...types.beta.sessions.beta_managed_agents_user_tool_result_event_params import ( Content as _SessionContent, BetaManagedAgentsUserToolResultEventParams, ) from ...types.beta.sessions.beta_managed_agents_user_custom_tool_result_event_params import ( BetaManagedAgentsUserCustomToolResultEventParams, ) if TYPE_CHECKING: from ..._client import AsyncAnthropic from ...resources.beta.sessions.events import AsyncEvents __all__ = [ "SessionToolRunner", "DispatchedToolCall", "DispatchedToolUseEvent", "DispatchedToolResultParams", "BetaAnyRunnableTool", "MANAGED_AGENTS_BETA", "DEFAULT_MAX_IDLE", # Re-exported for ``anthropic.lib.environments._worker``, which drives the # runner as an async context manager inside its own task group. "_run_session_tools", ] # Either sync or async runnable tool — the union the session-side runners # accept. ``Beta``-prefixed for consistency with the released # ``BetaRunnableTool`` (sync) / ``BetaAsyncRunnableTool`` (async) members it # unions; those two are unchanged. BetaAnyRunnableTool = Union[BetaRunnableTool, BetaAsyncRunnableTool] # The two tool-call event kinds the runner dispatches against the local tool # registry, and the matching result-event params it posts back for each: # # agent.tool_use -> user.tool_result (builtin agent_toolset tools) # agent.custom_tool_use -> user.custom_tool_result (custom, user-defined tools) # # ``agent.mcp_tool_use`` is intentionally absent — MCP tools run server-side and # the runner never sees a result to post for them. DispatchedToolUseEvent = Union[BetaManagedAgentsAgentToolUseEvent, BetaManagedAgentsAgentCustomToolUseEvent] DispatchedToolResultParams = Union[ BetaManagedAgentsUserToolResultEventParams, BetaManagedAgentsUserCustomToolResultEventParams, ] # A dispatch-queue item: the tool-call event paired with the confirmation # verdict that released it — ``"allow"`` for an ask-gated call the user # approved, ``None`` for a call that needed no confirmation. (Denied calls # never reach the queue.) Threading the verdict with the event keeps the # yielded ``DispatchedToolCall.confirmation`` tied to the verdict that actually # released the call rather than whatever ``_confirmations`` holds by the time # the tool finishes. _WorkItem = tuple[DispatchedToolUseEvent, Union[Literal["allow"], None]] # anthropic-beta gating Sessions access to self-hosted environments. The Sessions # resource auto-injects this header on its own requests; this constant is kept # for the work-item ``stop`` call the worker issues against the Work resource. MANAGED_AGENTS_BETA = "managed-agents-2026-04-01" STREAM_BACKOFF_START = 0.5 STREAM_BACKOFF_CAP = 10.0 # Outer per-tool-call timeout. This MUST stay strictly greater than the bash # tool's own ``agent_toolset.BASH_DEFAULT_TIMEOUT`` (120s). The bash tool wraps # its read in its own ``anyio.fail_after(BASH_DEFAULT_TIMEOUT)`` and, on # ``TimeoutError``, tears down the subprocess. If this outer deadline equalled # the inner one, the *outer* fail_after could win the race; anyio then raises # the parent scope's cancel as a plain ``Cancelled`` (NOT ``TimeoutError``), so # the bash tool's ``except TimeoutError`` cleanup never runs and its subprocess # is left alive with the timed-out command still queued — the next bash call # then reads stale output. The 30s margin gives the inner fail_after room to # fire and clean up before this one. (BashSession also now closes on any # outer-scope cancel as a belt-and-braces backstop, but these two timeouts must # still never be equal.) Invariant covered by # tests/lib/tools/test_session_runner.py::test_tool_timeout_exceeds_bash_default. TOOL_TIMEOUT = 150.0 SEND_RETRIES = 3 # Grace period, in seconds, that the runner keeps running after the session goes # idle with stop_reason ``end_turn`` before it stops; any new event in that # window resets it. ``max_idle=None`` disables it (run until the session ends). DEFAULT_MAX_IDLE = 60.0 log = logging.getLogger(__name__) class _IdleClock: """Tracks how long the session has been idle after an ``end_turn`` stop. :attr:`end_turn_at` is the monotonic timestamp of the most recent ``session.status_idle`` event with ``stop_reason.type == "end_turn"`` for which no newer event has since arrived; ``None`` whenever the session is not in that state. :meth:`SessionToolRunner._idle_watchdog` stops the runner once it has been set for ``max_idle`` seconds. Confirmation-gated calls pause the clock while they are unresolved: :meth:`hold` / :meth:`release` count them — from the moment a call is held awaiting its verdict until it is denied or, when allowed, until the dispatch loop has finished with it — and an :meth:`arm` landing while any are outstanding is deferred rather than applied. The last :meth:`release` applies a still-pending deferral so the runner can time out once nothing gated remains in flight. The clock is event-driven, not polled: every armed-state change signals the :attr:`wake` event so the watchdog wakes immediately instead of waiting out a poll interval. The watchdog captures :attr:`wake` *before* it reads :attr:`end_turn_at`, so a change landing between the read and the wait still wakes it. """ __slots__ = ("end_turn_at", "wake", "_holds", "_arm_deferred") def __init__(self) -> None: self.end_turn_at: float | None = None self.wake = anyio.Event() self._holds = 0 self._arm_deferred = False def _signal(self) -> None: # Wake any current waiter and arm a fresh event for the next wait. self.wake.set() self.wake = anyio.Event() def note_event(self, ev: object) -> None: """Arm the clock on an ``end_turn`` idle, disarm it on anything else. ``user.tool_confirmation`` events are neutral: they signal neither agent activity nor an idle, and their effect on the clock flows through :meth:`hold` / :meth:`release` instead — disarming here would discard the deferred arm the verdict is about to settle. """ ev_type = getattr(ev, "type", None) if ev_type == "user.tool_confirmation": return if ev_type == "session.status_idle" and getattr(getattr(ev, "stop_reason", None), "type", None) == "end_turn": self.arm() else: self.disarm() def arm(self) -> None: """(Re)start the idle countdown from now and wake the watchdog. Deferred while any gated call is held or in flight — stopping then would drop the held call when its verdict later arrives, or cut the runner off before a released call's result can drive the next turn. """ if self._holds: self._arm_deferred = True return self.end_turn_at = time.monotonic() self._signal() def disarm(self) -> None: """Cancel the idle countdown; only signals on an actual transition.""" self._arm_deferred = False if self.end_turn_at is not None: self.end_turn_at = None self._signal() def hold(self) -> None: """Pause the countdown while a gated call is held or in flight.""" self._holds += 1 if self.end_turn_at is not None: # Defensive: a hold taken while armed converts the running # countdown into a deferred one. self._arm_deferred = True self.end_turn_at = None self._signal() def release(self) -> None: """Drop one hold; the last release applies any deferred arm. Once nothing gated is held or in flight, a deferred ``end_turn`` countdown starts now (with a fresh grace window) so the runner can still time out — any newer event disarms it again as usual. """ self._holds -= 1 if self._holds == 0 and self._arm_deferred: self.arm() @dataclass(frozen=True) class DispatchedToolCall: """One tool call observed by :class:`SessionToolRunner`. Covers both tool-call event kinds — a builtin ``agent.tool_use`` and a custom ``agent.custom_tool_use``. The originating event is in :attr:`event` (with its input) and the posted-back result in :attr:`result`; ``name`` and ``tool_use_id`` are flat conveniences mirroring ``event``. """ event: DispatchedToolUseEvent """The full ``agent.tool_use`` / ``agent.custom_tool_use`` event the agent emitted. The tool input is ``event.input``.""" result: DispatchedToolResultParams | None """The result event the runner computed and attempted to post back to the session — ``user.tool_result`` for an ``agent.tool_use`` call, ``user.custom_tool_result`` for an ``agent.custom_tool_use`` call. The computed content is ``result["content"]``. ``None`` when the runner deliberately posted nothing: the tool name is not one this runner owns, so the ``tool_use_id`` was left pending for its owner, or the call was denied and never executed (see ``confirmation``). ``posted`` is ``False`` in either case.""" tool_use_id: str """Convenience: the id of the originating tool-call event — the same value as ``event.id`` for both event kinds.""" name: str """Convenience: the tool name — the same value as ``event.name``.""" is_error: bool """Convenience: whether the result is an error — the same value as ``result["is_error"]``. Always ``False`` for a skipped unowned call (the runner reaches no verdict on a tool it does not own; ``result`` is ``None``) and for a denied call (nothing ran, so there is no error to report; see ``confirmation``).""" posted: bool = True """``True`` if the result event made it to the session. ``False`` if all retries were exhausted or the server returned a permanent 4xx — in which case the session-side agent will *not* see this result and the consumer may want to surface that or retry at a higher level — and also ``False``, with ``result`` left ``None``, when the tool name is not one this runner owns and it deliberately posted nothing, leaving the ``tool_use_id`` pending for its owner (the split-client partial-fulfilment behavior), or when the call was denied and never executed (see ``confirmation``).""" confirmation: Literal["allow", "deny"] | None = None """The confirmation verdict that gated this call, if any. ``"allow"`` — the call required user confirmation (the server evaluated its permission to ``ask``, e.g. under an ``always_ask`` policy) and the matching ``user.tool_confirmation`` event approved it before the tool ran. ``"deny"`` — the user denied it, or the server itself evaluated the permission to ``deny``; the tool was never executed and nothing was posted (``result=None``, ``posted=False``, ``is_error=False``). ``None`` — the call needed no confirmation.""" def _scoped_client(client: AsyncAnthropic, environment_key: str | None) -> AsyncAnthropic: """Build the runner's request client. With an environment key, defer to :func:`_copy_client_with_bearer_auth` for a Bearer-only sub-client. Without one, layer the helper-telemetry header onto the caller's client via ``with_options`` (parent is not mutated). """ if environment_key is not None: return _copy_client_with_bearer_auth(client, auth_token=environment_key, helper="session-tool-runner") return client.with_options(default_headers=helper_header("session-tool-runner")) def _to_session_content(content: BetaFunctionToolResultType) -> list[_SessionContent]: """Bridge Messages-API tool-result content to the narrower Sessions-API content union. The two APIs share text/image/document/search_result block shapes but use distinct nominal TypedDicts; ToolReference blocks have no Sessions equivalent so they are stringified. """ if isinstance(content, str): return [{"type": "text", "text": content or "(no output)"}] out: list[_SessionContent] = [] for block in content: kind = block.get("type") if kind == "text": text = cast("str", block.get("text") or "(no output)") out.append({"type": "text", "text": text}) elif kind in ("image", "document", "search_result"): out.append(cast("_SessionContent", block)) else: out.append({"type": "text", "text": json.dumps(block)}) return out or [{"type": "text", "text": "(no output)"}] def _build_result_event( ev: DispatchedToolUseEvent, content: BetaFunctionToolResultType, is_error: bool, ) -> DispatchedToolResultParams: """Build the result-event params matching ``ev``'s tool-call kind. A custom tool call (``agent.custom_tool_use``) is answered with a ``user.custom_tool_result`` keyed by ``custom_tool_use_id``; a builtin tool call (``agent.tool_use``) with a ``user.tool_result`` keyed by ``tool_use_id``. Both use the codegen'd event-params TypedDicts. """ session_content = _to_session_content(content) if ev.type == "agent.custom_tool_use": custom_result: BetaManagedAgentsUserCustomToolResultEventParams = { "type": "user.custom_tool_result", "custom_tool_use_id": ev.id, "is_error": is_error, "content": session_content, } return custom_result builtin_result: BetaManagedAgentsUserToolResultEventParams = { "type": "user.tool_result", "tool_use_id": ev.id, "is_error": is_error, "content": session_content, } return builtin_result class SessionToolRunner: """Attach to a managed-agents session and dispatch its tool calls locally. The sessions-side counterpart to ``client.beta.messages.tool_runner``: an async iterable that, for each ``agent.tool_use`` or ``agent.custom_tool_use`` event the agent emits, executes the matching tool from ``tools``, posts the matching result event back (``user.tool_result`` for a builtin tool call, ``user.custom_tool_result`` for a custom one), and yields one :class:`DispatchedToolCall`. Internally drives event-stream reconnect (with capped backoff) and result posting via an ``anyio`` task group, so it works under both ``asyncio`` and ``trio``. Iteration ends when the session terminates (``session.status_terminated`` / ``session.deleted``), when the consumer breaks out of the loop, or — once the session has gone idle with ``stop_reason`` ``end_turn`` — when ``max_idle`` seconds elapse with no new event (any new event resets the countdown; it re-arms on the next ``end_turn`` idle). ``max_idle=None`` disables that last condition. On exit it runs each tool's optional cleanup: the ``close`` hook and, for tools defined as an (async) context manager, its ``__exit__`` / ``__aexit__``. It does **not** touch the work-item lease — wrap it in an :class:`~anthropic.lib.environments.EnvironmentWorker` for heartbeating / force-stop. Pass ``environment_key`` to authenticate the event stream / list / send calls with the self-hosted environment key (bearered, with the client's default ``x-api-key`` dropped); leave it unset to use the client's own credentials. A self-hosted session is commonly serviced by **two** clients at once: this runner inside the customer's sandbox (registered with the file/shell sandbox tools) and the customer's app backend (handling the agent's ``custom`` function tools). The Sessions API has a partial-fulfilment contract: when a session pauses on ``requires_action`` the pending tool-call ids can mix both kinds, and each client must post results **only** for the ids it owns and leave the rest pending for the other client. A tool-call event whose name is not in ``tools`` is therefore assumed to belong to the other client: the runner posts no result for it, does not mark it answered, and leaves the ``tool_use_id`` pending — but still yields a :class:`DispatchedToolCall` (``posted=False``, ``is_error=False``, ``result=None``) so the caller can observe the unowned dispatch. Tool calls the server gated behind user confirmation are **not** executed on arrival: an ``agent.tool_use`` event whose ``evaluated_permission`` is ``ask`` (e.g. a tool configured with the ``always_ask`` permission policy) is held until the matching ``user.tool_confirmation`` event arrives. An ``allow`` verdict releases the call to execute as normal; a ``deny`` verdict — or a call the server already evaluated to ``deny`` — is never executed and nothing is posted for it (the denial itself resolves the call server-side), but it is still yielded (``confirmation="deny"``, ``posted=False``, ``result=None``) so the caller can observe it. Usage:: from anthropic.lib.tools.agent_toolset import AgentToolContext, beta_agent_toolset_20260401 async with AgentToolContext(workdir="/workspace") as env: async for call in client.beta.sessions.events.tool_runner( work.data.id, tools=[*beta_agent_toolset_20260401(env), my_tool], ): print(f"{call.name} -> {'error' if call.is_error else 'ok'}") """ def __init__( self, client: AsyncAnthropic, session_id: str, *, tools: Sequence[BetaAnyRunnableTool], max_idle: float | None = DEFAULT_MAX_IDLE, environment_key: str | None = None, extra_headers: Headers | None = None, ) -> None: self.session_id = session_id self.tools: Sequence[BetaAnyRunnableTool] = tools self.max_idle = max_idle # All event stream / list / send requests are issued via this scoped # sub-client: Bearer-only when an environment key is set, otherwise the # caller's own client with the helper-telemetry header layered on. self._scoped = _scoped_client(client, environment_key) # Per-request passthrough headers: threaded into every event stream / # list / send via that call's ``extra_headers=`` (make_request_options) # — never assigned onto the client, so client state is not mutated. # Auth and ``x-stainless-helper`` come from the scoped sub-client and # the parent client's ``default_headers`` propagate via its # ``client.copy()``; per the SDK's standard ``extra_headers`` # precedence a caller header overrides the scoped client's same-named # default for that request (``x-stainless-helper`` is the exception — # a caller value appends to the runner's tag rather than replacing it), # so this is for caller passthrough (trace ids etc.), not auth. self.extra_headers = extra_headers async def __aiter__(self) -> AsyncIterator[DispatchedToolCall]: async with self._run() as calls: async for call in calls: yield call async def until_done(self) -> None: """Drive the runner to completion, discarding the per-call observations. Named to match ``BetaToolRunner.until_done`` (and to avoid colliding with :meth:`EnvironmentWorker.run`, which is a forever-loop): it returns once the session ends / goes idle, rather than running until cancelled. """ async for _ in self: pass # -- run lifecycle ------------------------------------------------------ @contextlib.asynccontextmanager async def _run(self) -> AsyncIterator[AsyncIterator[DispatchedToolCall]]: """Drive the session tool loop, yielding an iterator of :class:`DispatchedToolCall`. :meth:`__aiter__` (and the module-level :func:`_run_session_tools` shim used by ``EnvironmentWorker``) wrap this. Per-run state lives on ``self`` as private attributes so the loops below — :meth:`_stream_loop`, :meth:`_dispatch_loop`, :meth:`_reconcile`, :meth:`_idle_watchdog`, :meth:`_stop_watcher` — can mutate it as methods rather than threading a shared state object through free functions. """ self._events: AsyncEvents = self._scoped.beta.sessions.events log.info("session tool runner starting session_id=%s", self.session_id) self._tools_by_name: dict[str, BetaAnyRunnableTool] = tool_registry(self.tools) # ``_seen`` dedups tool-call events across the stream and the reconcile # pass (by event id); ``_answered`` holds the ids whose result post has # actually landed, so a failed post is retried on the next reconcile. self._seen: set[str] = set() self._answered: set[str] = set() # Confirmation gating (``always_ask`` tools): ``_confirmations`` records # every ``user.tool_confirmation`` verdict by ``tool_use_id``; # ``_awaiting_confirmation`` holds the tool-call events whose # ``evaluated_permission`` is ``ask`` and whose verdict has not arrived # yet — they are released to the dispatch loop (or resolved as denied) # by :meth:`_note_confirmation` / the next reconcile pass. Like ``_seen`` # and ``_answered``, ``_confirmations`` is per-session O(tool calls): # recorded verdicts persist for the life of the run. self._confirmations: dict[str, Literal["allow", "deny"]] = {} self._awaiting_confirmation: dict[str, DispatchedToolUseEvent] = {} self._stop = anyio.Event() self._idle_clock = _IdleClock() self._send_work, self._recv_work = anyio.create_memory_object_stream[_WorkItem]( max_buffer_size=100, ) self._send_results, self._recv_results = anyio.create_memory_object_stream[DispatchedToolCall]( max_buffer_size=math.inf, ) async def iterator() -> AsyncIterator[DispatchedToolCall]: # ``_recv_results`` is explicitly closed in the outer ``finally`` to # keep cleanup deterministic regardless of whether the consumer # iterated at all (e.g. ``async with runner._run(): pass``). async for call in self._recv_results: yield call try: # The outer ``CancelScope`` absorbs the task-group cancellation we # trigger in the ``finally`` below, so it doesn't surface to the # consumer as ``Cancelled``. with anyio.CancelScope(): async with anyio.create_task_group() as tg: # The stop watcher closes ``_send_work`` when ``_stop`` is # set so the dispatch loop's ``receive()`` raises # EndOfStream and the loop exits cleanly without us having # to inject a sentinel or race two awaitables. tg.start_soon(self._stop_watcher) tg.start_soon(self._stream_loop) tg.start_soon(self._dispatch_loop) if self.max_idle is not None: tg.start_soon(self._idle_watchdog) try: yield iterator() finally: # Signal every loop to exit. Most exit voluntarily on # ``_stop``; cancelling the task group's scope wakes # anything still blocked on an unrelated await (e.g. an # uncancellable test fake). anyio absorbs the resulting # cancel via the outer ``CancelScope``. self._stop.set() tg.cancel_scope.cancel() finally: # Explicitly close every stream so anyio doesn't warn on GC. # ``aclose`` is idempotent, so it's fine if the producer already # closed its end during normal shutdown. with anyio.CancelScope(shield=True): for stream in (self._recv_results, self._send_results, self._recv_work, self._send_work): try: await stream.aclose() except Exception: pass # Run each tool's optional cleanup (``close`` hook and, for # context-manager tools, ``__exit__`` / ``__aexit__``). Shielded so # the hooks survive the surrounding cancellation. with anyio.CancelScope(shield=True): for tool in self.tools: await aclose_runnable_tool(tool) # -- event-stream + reconcile ------------------------------------------ async def _reconcile(self) -> None: """Read full history and enqueue every tool-call event still unanswered. Two-pass: read the whole history before emitting so a tool-call whose result appears later in the same history is not re-dispatched. Pairs ``agent.tool_use`` with ``user.tool_result`` and ``agent.custom_tool_use`` with ``user.custom_tool_result`` when computing which calls are answered. """ pending: list[DispatchedToolUseEvent] = [] last_was_end_turn = False list_failed = False try: async for ev in self._events.list(self.session_id, limit=1000, extra_headers=self.extra_headers): if ev.type == "agent.tool_use" or ev.type == "agent.custom_tool_use": # Mark the event seen so the live stream doesn't re-enqueue it, but # decide whether it still needs executing from ``_answered``, not # ``_seen``: a call whose result post failed is seen-but-unanswered # and must be retried on the next reconcile pass rather than dropped. self._seen.add(ev.id) pending.append(ev) elif ev.type == "user.tool_result": self._answered.add(ev.tool_use_id) elif ev.type == "user.custom_tool_result": self._answered.add(ev.custom_tool_use_id) elif ev.type == "user.tool_confirmation": # Record the verdict only, before the pending pass below, so # a tool call whose confirmation appears later in the same # history is routed with its verdict already known. Releasing # a held call here as well would enqueue it a second time # when the routing pass reaches its tool_use event. Calls # already answered are never re-routed, so skip re-recording # their verdict on every reconcile. if ev.tool_use_id not in self._answered: self._confirmations[ev.tool_use_id] = ev.result last_was_end_turn = ( ev.type == "session.status_idle" and getattr(getattr(ev, "stop_reason", None), "type", None) == "end_turn" ) except Exception as e: # Pagination may have failed partway through; the ``_answered`` set # could be incomplete, so dispatching ``pending`` now would risk # re-running a tool whose result was on a page we never reached. # The next reconnect will retry the reconcile. Leave ``_idle_clock`` # untouched since the history we read may be incomplete. log.warning("reconcile list failed; skipping pending enqueue error=%s", e) list_failed = True if list_failed: # Roll back the ids we added to ``_seen`` so the live stream can # re-process them rather than silently dedup what we never finished # reading. for ev in pending: self._seen.discard(ev.id) return unanswered = [ev for ev in pending if ev.id not in self._answered] # Disarm before routing: enqueuing below can block on a full work # buffer while the clock may still be armed from before the reconnect. self._idle_clock.disarm() for ev in unanswered: await self._route_tool_event(ev) # A held call's verdict is normally applied by the routing pass above; # if its tool_use event fell outside the listed window the pass never # saw it, so apply the verdict to the held copy here. for held in [ev for ev in self._awaiting_confirmation.values() if ev.id in self._confirmations]: await self._apply_verdict(held, self._confirmations[held.id]) # Routing resolves denied calls in place (marking them answered) and # holds ask-gated calls for their ``user.tool_confirmation``. If the # most recent event in history is an ``end_turn`` idle and no tool work # is outstanding, the session is done — arm the idle clock so the # watchdog counts down even if that ``end_turn`` arrived during a # disconnect. Gated calls don't count as outstanding here whether still # held or just released to the dispatch queue: the clock holds them # (``_IdleClock.hold``), so this ``arm`` is deferred until they resolve. outstanding = [ ev for ev in unanswered if ev.id not in self._answered and ev.id not in self._awaiting_confirmation ] if last_was_end_turn and not outstanding: self._idle_clock.arm() async def _stream_loop(self) -> None: backoff = STREAM_BACKOFF_START while not self._stop.is_set(): try: # Open the stream *before* reconciling: with the stream already # attached, an event emitted in the gap between the list call # and the attach is delivered live instead of lost. ``_seen`` # dedups any overlap between the history and the live stream. async with await self._events.stream(self.session_id, extra_headers=self.extra_headers) as stream: await self._reconcile() async for ev in stream: backoff = STREAM_BACKOFF_START # Arm/disarm the idle clock: an ``end_turn`` idle starts # the grace countdown, any other event cancels it. The # clock itself defers the countdown while gated calls # are held or in flight (see ``_IdleClock.hold``). self._idle_clock.note_event(ev) if ev.type == "agent.tool_use" or ev.type == "agent.custom_tool_use": if ev.id not in self._seen: self._seen.add(ev.id) await self._route_tool_event(ev) elif ev.type == "user.tool_result": self._answered.add(ev.tool_use_id) elif ev.type == "user.custom_tool_result": self._answered.add(ev.custom_tool_use_id) elif ev.type == "user.tool_confirmation": await self._note_confirmation(ev) elif ev.type in ("session.status_terminated", "session.deleted"): log.info("session terminated") self._stop.set() return except TRANSIENT_ERRORS as e: if self._stop.is_set(): return if is_fatal_status_error(e): # No amount of backoff will fix a 401/403; bail out so the # consumer sees the runner exit instead of looping silently. log.error("stream failed permanently error=%s", e) self._stop.set() return log.warning("stream disconnected, reconnecting backoff=%.1fs error=%s", backoff, e) with anyio.move_on_after(backoff): await self._stop.wait() backoff = min(backoff * 2, STREAM_BACKOFF_CAP) # -- confirmation gating (always_ask tools) ------------------------------ async def _route_tool_event(self, ev: DispatchedToolUseEvent) -> None: """Enqueue ``ev`` for dispatch, honoring its evaluated permission. A builtin call the server gated behind user confirmation (``evaluated_permission == "ask"``, e.g. the ``always_ask`` policy) is held until the matching ``user.tool_confirmation`` event arrives instead of executing immediately. The gate fails closed: only an explicit ``allow`` verdict releases a gated call, a call the server already evaluated to ``deny`` is never executed regardless of any verdict, a stray ``deny`` verdict recorded for a call that never needed confirmation also resolves it as denied (any deny signal wins), and — because the wire can carry values newer than this SDK's types — an unrecognised permission is held like ``ask`` and an unrecognised verdict is treated as a denial, never dispatched. """ # ``getattr`` rather than an event-type check: today only # ``agent.tool_use`` carries ``evaluated_permission``, but if the field # ever lands on ``agent.custom_tool_use`` the gate must keep failing # closed rather than dispatch a gated call by event type. permission = getattr(ev, "evaluated_permission", None) verdict = self._confirmations.get(ev.id) if permission == "deny": # Server already denied the call: never execute it, even if a # (stray) allow verdict exists for the id. await self._resolve_denied(ev) return if verdict is None: if permission is None or permission == "allow": await self._send_work.send((ev, None)) elif ev.id not in self._awaiting_confirmation: # "ask" — or a permission value this SDK doesn't recognise, # which must not dispatch unconfirmed — waits for the user's # verdict. (Already-held: a reconcile after a reconnect # re-routes the call; keep the existing hold.) log.info( "tool %r requires user confirmation; holding tool_use_id=%s until user.tool_confirmation", ev.name, ev.id, ) self._awaiting_confirmation[ev.id] = ev self._idle_clock.hold() return await self._apply_verdict(ev, verdict) async def _note_confirmation(self, ev: BetaManagedAgentsUserToolConfirmationEvent) -> None: """Record an allow/deny verdict and release the held call it gates, if any.""" self._confirmations[ev.tool_use_id] = ev.result held = self._awaiting_confirmation.get(ev.tool_use_id) if held is None: # Nothing held: the verdict gates a call this runner has not seen # yet (or one it never gates, e.g. an ``agent.mcp_tool_use``). # Keeping it in ``_confirmations`` lets a later route of that call # resolve instantly. return await self._apply_verdict(held, ev.result) async def _apply_verdict(self, ev: DispatchedToolUseEvent, verdict: Literal["allow", "deny"]) -> None: """Dispatch or resolve a gated call according to the user's verdict. The idle-clock hold accounting lives here: a denial drops the held call's hold, while an allow keeps one hold on the call (taking it now if the verdict was already known when the call was routed, so it was never held) until the dispatch loop has finished with it — the countdown must not run over gated work that is still in flight. """ was_held = self._awaiting_confirmation.pop(ev.id, None) is not None if verdict == "allow": log.info("tool call confirmed tool=%s tool_use_id=%s", ev.name, ev.id) if not was_held: self._idle_clock.hold() await self._send_work.send((ev, "allow")) else: # "deny" — or a verdict value this SDK doesn't recognise, which # must not release the call (the gate fails closed). if was_held: self._idle_clock.release() await self._resolve_denied(ev) async def _resolve_denied(self, ev: DispatchedToolUseEvent) -> None: """Resolve a denied call without executing it. The denial itself resolves the call server-side — no result event will ever be posted for it — so mark it answered to keep reconcile from re-surfacing it and the idle accounting from waiting on it, then yield the observability call (nothing ran, nothing was posted). """ self._answered.add(ev.id) log.info("tool call denied; not executing tool=%s tool_use_id=%s", ev.name, ev.id) await self._surface_call( DispatchedToolCall( event=ev, result=None, tool_use_id=ev.id, name=ev.name, is_error=False, posted=False, confirmation="deny", ) ) async def _surface_call(self, call: DispatchedToolCall) -> None: """Yield ``call`` to the consumer, tolerating a consumer that left early. ``BrokenResourceError`` — the consumer broke out of the iterator; ``ClosedResourceError`` — the dispatch loop already closed the send side (possible for the deny path, which runs from the stream loop and can outlive the dispatch loop). Either way the underlying work already happened; only the observability event is lost. """ try: await self._send_results.send(call) except (anyio.BrokenResourceError, anyio.ClosedResourceError): pass # -- tool dispatch ------------------------------------------------------ async def _dispatch_loop(self) -> None: try: while True: try: ev, confirmation = await self._recv_work.receive() except anyio.EndOfStream: # Producer side closed — usually because ``_stop`` was set # (the idle watchdog or stream loop signalled it). return try: if ev.id not in self._answered: # Shielded execute so consumer-side cancellation can't # interrupt an in-flight tool. The result will still be # posted and the DispatchedToolCall enqueued before the # cancel propagates. with anyio.CancelScope(shield=True): await self._execute(ev, confirmation) finally: if confirmation == "allow": # The user-approved call is fully disposed of (executed, # or moot because it was answered elsewhere); drop the # idle-clock hold ``_apply_verdict`` kept on it. self._idle_clock.release() finally: # Closing the results stream signals the iterator that no more # results will arrive. Wrapped in a shield because we're often in a # cancellation path on the way out. with anyio.CancelScope(shield=True): await self._send_results.aclose() async def _execute(self, ev: DispatchedToolUseEvent, confirmation: Literal["allow"] | None) -> None: """Run ``ev``'s tool, post its result, and surface the dispatched call. ``confirmation`` is the verdict that released the call onto the work queue — ``"allow"`` for an ask-gated call the user approved, ``None`` for a call that needed no confirmation. (Denied calls never reach this method; ``_resolve_denied`` surfaces them.) """ log.info("executing tool tool=%s tool_use_id=%s", ev.name, ev.id) tool = self._tools_by_name.get(ev.name) tool_result: DispatchedToolResultParams | None if tool is None: # Skip unowned (split-client partial fulfilment): a name this # runner is not registered for belongs to the other client # servicing this session (typically the customer's app backend # handling custom tools). Post NO result, do not mark it answered, # and leave the tool_use_id pending for its owner — claiming it # would corrupt the conversation (the model would read "not # implemented" as the tool output while the real result from the # other client arrives afterwards). Still yield the call so the # caller can observe the unowned dispatch; nothing was sent, so # ``posted`` and ``is_error`` stay False and ``result`` is None. # The id stays unanswered, so reconcile keeps it out of the # idle/end-turn accounting and re-surfaces it after a reconnect # until its owner answers it. log.info( "tool %r not owned by this runner; leaving tool_use_id=%s pending for its owner", ev.name, ev.id, ) tool_result = None is_error = False sent = False else: content: BetaFunctionToolResultType is_error = False input_ = dict(ev.input) try: with anyio.fail_after(TOOL_TIMEOUT): content = await run_runnable_tool(tool, input_) except TimeoutError: content = f"tool {ev.name!r} timed out" is_error = True except ToolError as e: content = tool_error_content(e) is_error = True except Exception as e: log.exception("tool %s raised", ev.name) content = tool_error_content(e) is_error = True tool_result = _build_result_event(ev, content, is_error) sent = await self._send_result(tool_result, ev.id) await self._surface_call( DispatchedToolCall( event=ev, result=tool_result, tool_use_id=ev.id, name=ev.name, is_error=is_error, posted=sent, confirmation=confirmation, ) ) async def _send_result(self, tool_result: DispatchedToolResultParams, tool_use_id: str) -> bool: """Post ``tool_result`` back to the session, retrying transient failures. ``tool_use_id`` is the originating tool-call event id — passed explicitly because the result params key it differently (``tool_use_id`` vs ``custom_tool_use_id``) depending on the kind. """ last_err: Exception | None = None for i in range(SEND_RETRIES): try: await self._events.send( self.session_id, events=[tool_result], extra_headers=self.extra_headers, ) self._answered.add(tool_use_id) return True except TRANSIENT_ERRORS as e: last_err = e if is_fatal_status_error(e): break # Don't sleep after the final attempt — there is no retry to wait for. if i < SEND_RETRIES - 1: await anyio.sleep(i + 1) log.error("failed to send tool result tool_use_id=%s error=%s", tool_use_id, last_err) return False # -- background watchers ----------------------------------------------- async def _idle_watchdog(self) -> None: """Stop the runner once the session has been idle (``end_turn``) for ``max_idle`` seconds with no new events. Event-driven: it blocks on the idle clock's wake event rather than polling. Capturing ``clock.wake`` *before* reading ``clock.end_turn_at`` closes the race where the clock changes between the read and the wait. """ max_idle = self.max_idle assert max_idle is not None clock = self._idle_clock while not self._stop.is_set(): wake = clock.wake at = clock.end_turn_at if at is None: # Not armed: block until the clock arms (or the runner stops). await _wait_first(wake, self._stop) continue remaining = max_idle - (time.monotonic() - at) if remaining <= 0: log.info("session idle after end_turn for %.0fs; stopping", max_idle) self._stop.set() return # Armed: wait out the remaining grace, waking early if the clock # changes (disarmed, or re-armed with a fresh timestamp) or the # runner stops. Either way, loop back and re-evaluate. with anyio.move_on_after(remaining): await _wait_first(wake, self._stop) async def _stop_watcher(self) -> None: """When ``_stop`` is set, close the work stream so :meth:`_dispatch_loop` exits.""" await self._stop.wait() await self._send_work.aclose() async def _wait_first(*events: anyio.Event) -> None: """Return as soon as any of ``events`` is set.""" async with anyio.create_task_group() as tg: async def _waiter(ev: anyio.Event) -> None: await ev.wait() tg.cancel_scope.cancel() for ev in events: tg.start_soon(_waiter, ev) @contextlib.asynccontextmanager async def _run_session_tools( client: AsyncAnthropic, session_id: str, *, tools: Sequence[BetaAnyRunnableTool], max_idle: float | None = DEFAULT_MAX_IDLE, environment_key: str | None = None, extra_headers: Headers | None = None, ) -> AsyncIterator[AsyncIterator[DispatchedToolCall]]: """Internal: drive a :class:`SessionToolRunner` as an async context manager. Kept as a thin module-level shim because :class:`~anthropic.lib.environments.EnvironmentWorker` enters the runner inside its own task group and wants the context-manager shape for deterministic cleanup. New code should iterate :class:`SessionToolRunner` directly. """ runner = SessionToolRunner( client, session_id, tools=tools, max_idle=max_idle, environment_key=environment_key, extra_headers=extra_headers, ) async with runner._run() as calls: # noqa: SLF001 yield calls anthropic-sdk-python-0.120.2/src/anthropic/lib/tools/_skills.py000066400000000000000000000255551523216435200244560ustar00rootroot00000000000000"""Skill download + archive extraction for the agent toolset. Split out from ``agent_toolset`` because fetching a session agent's skills and safely unpacking a (possibly third-party) archive is a distinct concern from the tool implementations themselves. """ from __future__ import annotations import os import shutil import logging import tarfile import zipfile import tempfile from typing import TYPE_CHECKING from pathlib import Path, PurePosixPath from functools import partial import anyio from anyio.to_thread import run_sync if TYPE_CHECKING: from ..._client import AsyncAnthropic __all__ = ["download_session_skills"] # Skill dirs hold downloaded, possibly third-party content — keep them # owner-only rather than inheriting whatever the process umask happens to be. _SKILL_DIR_MODE = 0o700 log = logging.getLogger("anthropic.lib.tools.agent_toolset") def _within(child: Path, root: Path) -> bool: """True if ``child`` is ``root`` or a path inside it (both already resolved).""" try: child.relative_to(root) except ValueError: return False return True def _safe_member_name(name: str) -> str: """Return ``name`` as a confined relative path, or raise on path-traversal. Strips ``.`` components; rejects absolute paths and any ``..`` component outright (those only appear in malicious archives). Returns ``""`` for entries that resolve to nothing (e.g. ``"./"``) — the caller should skip those. """ norm = name.replace("\\", "/") if norm.startswith("/") or PurePosixPath(norm).is_absolute(): raise ValueError(f"refusing archive member with absolute path {name!r}") parts = [p for p in PurePosixPath(norm).parts if p != "."] if any(p == ".." for p in parts): raise ValueError(f"refusing archive member with '..' component: {name!r}") return str(PurePosixPath(*parts)) if parts else "" def _archive_top_dir(names: list[str]) -> str: """Return the single top-level directory shared by every archive entry, or ``""`` if the entries don't all live under one common directory. Skill bundles are packaged wrapped in one directory named after the skill (e.g. ``pdf/SKILL.md``, ``pdf/scripts/...``). The extractor strips that wrapper so the contents land directly in the skill's destination directory instead of a redundant nested ``//`` level. """ tops: set[str] = set() has_nested = False for n in names: parts = PurePosixPath(n).parts if not parts: continue tops.add(parts[0]) if len(parts) > 1: has_nested = True return next(iter(tops)) if len(tops) == 1 and has_nested else "" def _strip_top(safe: str, top: str) -> str: """Drop the leading ``top`` component from ``safe`` (an already-confined relative path). Returns ``""`` for the bare top-dir entry itself.""" if not top: return safe parts = PurePosixPath(safe).parts if parts and parts[0] == top: rest = parts[1:] return str(PurePosixPath(*rest)) if rest else "" return safe def _archive_file_mode(src_mode: int) -> int: """Reduce an archive entry's Unix mode to ``0o755`` if it is executable, ``0o644`` otherwise. Skill bundles can ship executable scripts (e.g. ``scripts/foo.sh``), so the execute bit recorded in the archive must survive extraction or invoking the script directly fails with permission denied. The mode is deliberately collapsed to one of two values: this preserves "is it executable" while never propagating setuid/setgid/sticky or group/other-write bits from a (possibly third-party) archive. """ return 0o755 if src_mode & 0o111 else 0o644 def _extract_skill_archive(archive_path: Path, dest: Path) -> None: """Extract a skill download (a zip or tar.* archive) from disk into ``dest``. Skill bundles are wrapped in a single directory named after the skill; that wrapper is stripped so files land directly under ``dest`` rather than a redundant ``dest//`` level. Skills can be third-party, so this refuses any member that would escape ``dest`` (zip-slip / tar-slip) and skips symlink/hardlink/device members in tar archives. """ dest.mkdir(parents=True, exist_ok=True, mode=_SKILL_DIR_MODE) root = dest.resolve() if zipfile.is_zipfile(archive_path): with zipfile.ZipFile(archive_path) as zf: infos = zf.infolist() # Compute the wrapper dir from the same confined names the loop # uses, so a malicious name still raises before anything is written. safe_names = [s for info in infos if (s := _safe_member_name(info.filename))] top = _archive_top_dir(safe_names) for info in infos: safe = _strip_top(_safe_member_name(info.filename), top) if not safe: continue target = (root / safe).resolve() if not _within(target, root): raise ValueError(f"refusing to extract unsafe zip member {info.filename!r}") if info.is_dir(): target.mkdir(parents=True, exist_ok=True) continue target.parent.mkdir(parents=True, exist_ok=True) with zf.open(info) as src, open(target, "wb") as out: shutil.copyfileobj(src, out) # ``external_attr``'s high 16 bits hold the Unix mode; it is 0 # for archives created without Unix attrs -> non-executable. os.chmod(target, _archive_file_mode(info.external_attr >> 16)) return # tarfile.open with "r:*" transparently handles tar / tar.gz / tar.bz2 / tar.xz. with tarfile.open(archive_path, mode="r:*") as tf: members = [m for m in tf.getmembers() if not (m.issym() or m.islnk() or m.isdev())] safe_names = [s for m in members if (s := _safe_member_name(m.name))] top = _archive_top_dir(safe_names) for member in members: safe = _strip_top(_safe_member_name(member.name), top) if not safe: continue target = (root / safe).resolve() if not _within(target, root): raise ValueError(f"refusing to extract unsafe tar member {member.name!r}") if member.isdir(): target.mkdir(parents=True, exist_ok=True) continue target.parent.mkdir(parents=True, exist_ok=True) extracted = tf.extractfile(member) if extracted is None: continue with extracted as src, open(target, "wb") as out: shutil.copyfileobj(src, out) os.chmod(target, _archive_file_mode(member.mode)) async def _resolve_skill_version(client: AsyncAnthropic, skill_id: str, version: str) -> str: """Resolve ``version`` to the concrete numeric timestamp the ``/v1/skills/{id}/versions/{version}`` endpoints require. ``session.agent.skills[].version`` may be an alias such as ``"latest"``, which those endpoints reject — so list the skill's versions and pick the newest. Numeric versions are returned unchanged. """ if version.isdigit(): return version newest: str | None = None async for v in client.beta.skills.versions.list(skill_id): if v.version.isdigit() and (newest is None or int(v.version) > int(newest)): newest = v.version if newest is None: raise ValueError(f"skill {skill_id!r} has no concrete version to resolve {version!r} against") return newest async def download_session_skills( client: AsyncAnthropic, *, session_id: str, workdir: str | os.PathLike[str] ) -> list[Path]: """Download the session agent's skills into ``{workdir}/skills//``. Looks up the session's resolved agent, and for each skill fetches its files via ``client.beta.skills.versions.download`` and extracts the archive under a directory named after the skill. The archive is streamed to a temp file rather than buffered whole in memory. A failure on one skill is logged and does not block the others. Returns the list of skill directories that were created, so the caller can remove them when the workdir is torn down. """ # The sessions/skills resources inject their anthropic-beta headers # (managed-agents / skills) themselves — no need to pass `betas=` here. session = await client.beta.sessions.retrieve(session_id) skills_root = Path(await (anyio.Path(workdir) / "skills").resolve()) # ``skills_root`` is created lazily by the extraction below — don't create it # up front so an agent with no skills leaves no stray directory behind. downloaded: list[Path] = [] for skill in session.agent.skills: try: version_id = await _resolve_skill_version(client, skill.skill_id, skill.version) version = await client.beta.skills.versions.retrieve(version_id, skill_id=skill.skill_id) # The directory is the skill's name, but reduce it to a single safe # path component so a hostile name can't escape skills_root. dirname = os.path.basename(version.name.strip()) or skill.skill_id if dirname in ("", ".", ".."): dirname = skill.skill_id dest = Path(await (anyio.Path(skills_root) / dirname).resolve()) if not _within(dest, skills_root): log.warning("skill name %r escapes the skills dir; skipping", version.name) continue adest = anyio.Path(dest) if await adest.is_symlink(): await adest.unlink() # ``shutil.rmtree`` is blocking; keep it off the event loop. await run_sync(partial(shutil.rmtree, dest, ignore_errors=True)) await _download_and_extract(client, skill.skill_id, version_id, dest) downloaded.append(dest) log.info("downloaded skill skill_id=%s version=%s -> %s", skill.skill_id, version_id, dest) except Exception as e: log.warning("failed to download skill skill_id=%s: %s", skill.skill_id, e) return downloaded async def _download_and_extract(client: AsyncAnthropic, skill_id: str, version_id: str, dest: Path) -> None: """Stream the skill archive to a temp file, then extract it into ``dest``.""" await anyio.Path(dest.parent).mkdir(parents=True, exist_ok=True, mode=_SKILL_DIR_MODE) fd, tmp_name = await run_sync(partial(tempfile.mkstemp, prefix=".skill-", suffix=".archive", dir=dest.parent)) os.close(fd) tmp = anyio.Path(tmp_name) try: async with client.beta.skills.versions.with_streaming_response.download( version_id, skill_id=skill_id ) as archive: await archive.stream_to_file(tmp_name) # zipfile / tarfile are blocking; keep them off the event loop. await run_sync(_extract_skill_archive, Path(tmp_name), dest) finally: await tmp.unlink(missing_ok=True) anthropic-sdk-python-0.120.2/src/anthropic/lib/tools/_tool_dispatch.py000066400000000000000000000135101523216435200257750ustar00rootroot00000000000000"""Shared tool-dispatch helpers for the tool runners. Both ``client.beta.messages.tool_runner`` (the Messages tool runner) and ``client.beta.sessions.events.tool_runner`` (the sessions-side :class:`~anthropic.lib.tools._beta_session_runner.SessionToolRunner`) do the same three small things: index the supplied tools by name, run a runnable tool over a JSON input, and turn an exception raised by a tool into tool-result content. Those steps are factored out here so the two runners stay consistent instead of each carrying its own copy. Consumed by the runner helpers only. """ from __future__ import annotations import inspect from typing import Union, TypeVar, Iterable, Awaitable from typing_extensions import Protocol from ._beta_functions import ToolError, BetaFunctionToolResultType from ...types.beta.beta_message_param import BetaMessageParam from ...types.beta.beta_content_block_param import BetaContentBlockParam from ...types.beta.beta_request_tool_removal_block_param import ( Tool as _ToolChangeReference, BetaRequestToolRemovalBlockParam, ) from ...types.beta.beta_request_tool_addition_block_param import BetaRequestToolAdditionBlockParam __all__ = ["tool_registry", "tool_error_content", "run_runnable_tool", "available_tool_names"] class _NamedTool(Protocol): """Anything with a ``name`` — the shape :func:`tool_registry` indexes on.""" @property def name(self) -> str: ... class _CallableTool(Protocol): """A runnable tool: ``call`` may be sync or async (it returns either the result or an awaitable of it).""" def call(self, input: object) -> Union[BetaFunctionToolResultType, Awaitable[BetaFunctionToolResultType]]: ... NamedToolT = TypeVar("NamedToolT", bound=_NamedTool) def tool_registry(tools: Iterable[NamedToolT]) -> dict[str, NamedToolT]: """Index ``tools`` by their ``name`` for O(1) dispatch lookup. On a duplicate name the later tool wins, matching a plain dict comprehension. """ return {tool.name: tool for tool in tools} def available_tool_names(messages: Iterable[BetaMessageParam], tool_names: Iterable[str]) -> set[str]: """Fold mid-conversation ``tool_removal`` / ``tool_addition`` blocks over the locally runnable ``tool_names``. Only ``role: "system"`` messages carry these blocks, and only a ``tool_reference`` can name a locally runnable tool — MCP references are executed server-side, so they (and any unknown block/reference type) are ignored rather than raising. """ available = set(tool_names) for message in messages: content = message["content"] if message["role"] != "system" or isinstance(content, str): continue for block in content: _apply_tool_change(block, available) return available def _apply_tool_change(block: BetaContentBlockParam, available: set[str]) -> None: """Apply a single ``tool_removal`` / ``tool_addition`` block to ``available``. A ``mid_conv_system`` block's ``content`` is limited by the API schema to ``text`` / ``tool_addition`` / ``tool_removal``, so exactly one level is walked (no deeper nesting exists); every other block type is a no-op. """ if not isinstance(block, dict): # ``BetaContentBlockParam`` also admits response-side content-block # models; ``tool_removal`` / ``tool_addition`` are request-only # TypedDicts, so a non-dict block is never one of them. return if block["type"] == "tool_removal" or block["type"] == "tool_addition": _apply_tool_reference_change(block, available) elif block["type"] == "mid_conv_system": for inner in block["content"]: # schema-bounded to text/tool_addition/tool_removal: one level, no recursion if inner["type"] == "tool_removal" or inner["type"] == "tool_addition": _apply_tool_reference_change(inner, available) else: pass # other/unknown block types are ignored (forward compatibility) def _apply_tool_reference_change( block: Union[BetaRequestToolRemovalBlockParam, BetaRequestToolAdditionBlockParam], available: set[str] ) -> None: """Fold one ``tool_removal`` / ``tool_addition`` block into ``available``.""" name = _referenced_tool_name(block["tool"]) if name is None: return if block["type"] == "tool_removal": available.discard(name) # removing an absent name is a set no-op else: available.add(name) # add unconditionally: dispatch still requires a registry hit def _referenced_tool_name(ref: _ToolChangeReference) -> str | None: """The locally runnable tool name a tool-change reference resolves to. Only ``tool_reference`` names a runnable tool; ``mcp_tool_reference`` / ``mcp_toolset_reference`` execute server-side and unknown reference types are ignored (forward compatibility), so all of those resolve to ``None``. """ if ref["type"] == "tool_reference": return ref["name"] return None def tool_error_content(exc: BaseException) -> BetaFunctionToolResultType: """Render an exception raised by a tool as tool-result content. A :class:`ToolError` carries its own structured content; anything else is rendered with ``repr`` (which, unlike ``str``, keeps the exception type). The caller owns the ``is_error`` flag and any logging. """ if isinstance(exc, ToolError): return exc.content return repr(exc) async def run_runnable_tool(tool: _CallableTool, input: dict[str, object]) -> BetaFunctionToolResultType: """Call ``tool`` with ``input``, awaiting the result if the tool is async. Bridges the sync (:class:`~anthropic.lib.tools.BetaFunctionTool`) and async (:class:`~anthropic.lib.tools.BetaAsyncFunctionTool`) runnable-tool shapes behind a single ``await``. """ result = tool.call(input) if inspect.isawaitable(result): return await result return result anthropic-sdk-python-0.120.2/src/anthropic/lib/tools/agent_toolset.py000066400000000000000000001125651523216435200256630ustar00rootroot00000000000000"""Reference implementations of the ``agent_toolset_20260401`` tools — ``bash``, ``read``, ``write``, ``edit``, ``glob``, ``grep`` — plus the workdir/skills :class:`AgentToolContext`. This sits next to the other ``lib/tools`` helpers (the Messages tool runner, the memory tool, …). Importing it pulls in ``subprocess`` etc., so it is kept out of ``anthropic.lib.tools.__init__`` — depend on it explicitly (``from anthropic.lib.tools.agent_toolset import beta_agent_toolset_20260401``). The result of :func:`beta_agent_toolset_20260401` is a plain ``list[BetaAsyncFunctionTool]`` — *async* function tools, so it is for the **async** runners only: ``client.beta.sessions.events.tool_runner(...)`` (the ``SessionToolRunner``, always async) for a managed-agents session, or — via the :class:`~anthropic.lib.environments.EnvironmentWorker` — the self-hosted environment worker. The sync ``Anthropic`` ``messages.tool_runner`` accepts ``BetaRunnableTool``, which excludes the async function tools this returns, so it cannot consume this toolset. .. warning:: ``bash`` is **stateful**: it owns a persistent ``/bin/bash`` subprocess that is only torn down by its ``close`` cleanup hook. Only ``SessionToolRunner`` (and the ``EnvironmentWorker`` built on it) invoke that hook. The Messages ``client.beta.messages.tool_runner(...)`` does **not** call ``close``, so handing this toolset to the Messages tool runner leaks the bash subprocess (one orphaned shell per run). Run stateful tools under ``client.beta.sessions.events.tool_runner(...)`` / the environment worker, or drop ``bash`` from the toolset before using the Messages tool runner. Trust model: the file tools confine to ``workdir`` (symlink-aware) and are safe without a sandbox; ``bash`` is unrestricted and should run inside one. See :class:`AgentToolContext`. """ from __future__ import annotations import os import re import uuid import base64 import shutil import logging import subprocess from stat import S_ISREG from typing import TYPE_CHECKING, Any, List, Optional, NamedTuple, cast from pathlib import Path, PurePosixPath from functools import partial from itertools import islice from contextlib import asynccontextmanager from dataclasses import field, dataclass from collections.abc import Mapping, Callable, Awaitable, AsyncIterator import anyio import anyio.abc from anyio.to_thread import run_sync from ._skills import _within, download_session_skills from ..._types import NotGiven, not_given from ..._utils import is_given from ...types.beta import ( BetaManagedAgentsAgentToolset20260401BashInput, BetaManagedAgentsAgentToolset20260401EditInput, BetaManagedAgentsAgentToolset20260401GlobInput, BetaManagedAgentsAgentToolset20260401GrepInput, BetaManagedAgentsAgentToolset20260401ReadInput, BetaManagedAgentsAgentToolset20260401WriteInput, ) from ._beta_functions import ( ToolError, BetaContent, BetaAsyncFunctionTool, BetaFunctionToolResultType, beta_async_tool, ) if TYPE_CHECKING: from ..._client import AsyncAnthropic __all__ = [ "AgentToolContext", "BashSession", "BashResult", "resolve_path", "beta_agent_toolset_20260401", "beta_bash_tool", "beta_read_tool", "beta_write_tool", "beta_edit_tool", "beta_glob_tool", "beta_grep_tool", ] BASH_OUTPUT_LIMIT = 100 * 1024 BASH_DEFAULT_TIMEOUT = 120.0 DEFAULT_MAX_FILE_BYTES = 256 * 1024 READ_MAX_BYTES = DEFAULT_MAX_FILE_BYTES # For backwards compat only. # Default image/PDF caps for the binary ``read`` path (overridable on # :class:`AgentToolContext`, same shape as ``max_file_bytes``). The API # enforces a per-image limit on the *encoded* (base64) form and a total # request-size limit that the raw-PDF cap stays under after the ~4/3 base64 # inflation; an oversized block would be rejected at request time, so reject # it here with a clear error instead. The spec doesn't publish these limits, so # they can't be codegen'd; if the API raises them, the only cost of these going # stale is rejecting a file early that the API would now accept — bump them # here (or override them on the context) when that happens. DEFAULT_MAX_IMAGE_BASE64_BYTES = 5 * 1024 * 1024 DEFAULT_MAX_PDF_BYTES = 20 * 1024 * 1024 READ_IMAGE_MAX_BASE64_BYTES = DEFAULT_MAX_IMAGE_BASE64_BYTES # For backwards compat only. READ_PDF_MAX_BYTES = DEFAULT_MAX_PDF_BYTES # For backwards compat only. # Extension → media type for files ``read`` returns as base64 content blocks # rather than text. The supported media types ARE codegen'd # (``BetaBase64ImageSourceParam`` / ``BetaBase64PDFSourceParam``); a test pins # this map's values to those literals, so a spec change that adds or removes a # media type fails CI until the map is updated. Not user-configurable (yet). _BINARY_MEDIA_TYPES = { ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif", ".webp": "image/webp", ".pdf": "application/pdf", } GREP_OUTPUT_LIMIT = 100 * 1024 GREP_MAX_LINE_LENGTH = 2000 GLOB_RESULT_LIMIT = 200 WALK_MAX_ENTRIES = 50_000 _ANSI = re.compile(r"\x1b\[[0-9;?]*[ -/]*[@-~]") def _resolve_max_bytes(configured: int | None | NotGiven, default: int = DEFAULT_MAX_FILE_BYTES) -> int | None: """Resolve a configured cap to an effective size limit. ``not_given`` selects ``default``; ``None`` disables the size check (uncapped); a positive int is the cap. Governs only the size guard — callers still reject non-regular files. """ return configured if is_given(configured) else default log = logging.getLogger("anthropic.lib.tools.agent_toolset") def _default_bash_env() -> dict[str, str]: """The environment for the bash subprocess, with the runner's own credentials scrubbed. The bash tool runs model-issued commands, so it must never inherit the runner's ``ANTHROPIC_*`` variables (API key, environment key, per-work session tokens): a prompt-injected ``echo $ANTHROPIC_API_KEY`` would otherwise land the credential straight in the session transcript. Passing an explicit ``env`` to :class:`AgentToolContext` does NOT add to this default — it FULLY REPLACES it. The provided mapping becomes the entire bash environment verbatim; nothing here is merged in, so callers who want the scrubbed process environment plus extras must build that mapping themselves. """ return {k: v for k, v in os.environ.items() if not k.startswith("ANTHROPIC_")} def _fs_error(op: str, file_path: str, e: OSError) -> ToolError: """Map a filesystem ``OSError`` to a consistent, runtime-independent message. The raw ``OSError`` string is platform-specific (``[Errno 2] ENOENT: ...``); normalise the common cases so the model sees the same wording everywhere. """ if isinstance(e, FileNotFoundError): reason = "no such file or directory" elif isinstance(e, NotADirectoryError): reason = "not a directory" elif isinstance(e, IsADirectoryError): reason = "is a directory" elif isinstance(e, PermissionError): reason = "permission denied" elif isinstance(e, FileExistsError): reason = "file already exists" else: reason = (e.strerror or "i/o error").lower() return ToolError(f"{op}: {file_path}: {reason}") def _empty_skill_dirs() -> list[Path]: return [] @dataclass class AgentToolContext: """Workdir + path-policy for the agent toolset. Trust model — two tiers: - The file tools (:func:`beta_read_tool`, :func:`beta_write_tool`, :func:`beta_edit_tool`, :func:`beta_glob_tool`, :func:`beta_grep_tool`) resolve paths against ``workdir`` and reject escapes unless ``unrestricted_paths`` is set. :func:`resolve_path` follows every symlink (including the leaf, even a dangling one) before the check and returns that canonical path for the operation, so a symlink inside the workdir that points outside it can neither pass the check nor be followed afterwards — a real boundary, consistent with the memory tool, so the file tools are safe to use without a sandbox. - :func:`beta_bash_tool` runs an unrestricted ``/bin/bash`` regardless of ``unrestricted_paths``. Confinement for it must come from the OS layer (e.g. a self-hosted environment runner). Attributes: workdir: Base directory for resolving relative tool paths. Defaults to :func:`os.getcwd` captured when the context is constructed (TS parity: ``process.cwd()`` at construction), so a ``chdir`` between constructing this context and the first tool call does not move where paths resolve. Pass an explicit path to override. unrestricted_paths: When ``False`` (default), the file tools reject paths that resolve outside ``workdir``. Does **not** constrain :func:`beta_bash_tool`. env: Optional environment for the bash subprocess. When unset, the bash tool inherits the process environment with the runner's ``ANTHROPIC_*`` credentials scrubbed. When provided, it FULLY REPLACES that default environment — the mapping is used verbatim and is NOT merged with or added to the scrubbed process environment. To keep the defaults plus extra vars, build the combined mapping yourself before passing it. max_file_bytes: Size cap for the ``read`` and ``edit`` tools, which both load the whole file into memory. ``not_given`` (default) uses the built-in 256 KiB cap; a positive int sets a custom cap; ``None`` disables the cap entirely. Disabling it reintroduces the OOM risk on a model-controlled path, so pass ``None`` only when the sandbox can absorb arbitrarily large files. The non-regular-file (FIFO/device) guard always applies regardless of this value. Image/PDF files, which ``read`` returns as base64 content blocks, are not subject to the 256 KiB default (``max_image_base64_bytes`` / ``max_pdf_bytes`` govern instead), but an explicit positive cap binds them too. max_image_base64_bytes: Cap on the *base64-encoded* size of an image ``read`` returns as a content block. ``not_given`` (default) uses the built-in 5 MiB cap — a memory bound plus the API's per-image limit; a positive int overrides it; ``None`` disables it (only ``max_file_bytes`` / the API's own limit then apply). max_pdf_bytes: Cap on the raw size of a PDF ``read`` returns as a document block. ``not_given`` (default) uses the built-in 20 MiB cap; a positive int overrides it; ``None`` disables it. """ # ``default_factory`` (not a literal "." ) so the cwd is snapshotted at # *construction* time, not resolved lazily at first use — a chdir in # between must not change where tools resolve paths (TS parity). workdir: str | os.PathLike[str] = field(default_factory=os.getcwd) unrestricted_paths: bool = False # When ``client`` and ``session_id`` are both set, entering the context # manager fetches the session's resolved agent and downloads each of its # skills into ``{workdir}/skills//`` before any tool runs. client: AsyncAnthropic | None = None session_id: str | None = None env: Optional[Mapping[str, str]] = None max_file_bytes: int | None | NotGiven = not_given max_image_base64_bytes: int | None | NotGiven = not_given max_pdf_bytes: int | None | NotGiven = not_given _bash: BashSession | None = field(default=None, init=False, repr=False) # Skill directories downloaded by ``setup_skills``; removed again on # ``__aexit__`` so a context doesn't leave downloaded skills behind. _skill_dirs: list[Path] = field(default_factory=_empty_skill_dirs, init=False, repr=False) async def bash(self) -> BashSession: if self._bash is None: self._bash = await BashSession.start(self.workdir, env=self.env) return self._bash async def close(self) -> None: if self._bash is not None: await self._bash.close() self._bash = None async def setup_skills(self) -> None: """Download the session agent's skills into ``{workdir}/skills//``. No-op unless both :attr:`client` and :attr:`session_id` are set. The download + safe archive extraction lives in :mod:`anthropic.lib.tools._skills`. """ if self.client is None or self.session_id is None: return self._skill_dirs = await download_session_skills(self.client, session_id=self.session_id, workdir=self.workdir) async def _cleanup_skills(self) -> None: """Remove the skill directories :meth:`setup_skills` downloaded. Only the directories this context created are removed — a pre-existing ``{workdir}/skills`` tree is left untouched. """ for skill_dir in self._skill_dirs: try: # ``shutil.rmtree`` is blocking; keep it off the event loop. await run_sync(partial(shutil.rmtree, skill_dir, ignore_errors=True)) except Exception as e: log.warning("failed to remove downloaded skill dir %s: %s", skill_dir, e) self._skill_dirs = [] async def __aenter__(self) -> AgentToolContext: await self.setup_skills() return self async def __aexit__(self, *exc: object) -> None: try: await self.close() finally: await self._cleanup_skills() def resolve_path(ctx: AgentToolContext, p: str) -> Path: """Resolve ``p`` against the workdir; reject results that escape it. Absolute and relative inputs go through the same canonicalise-then-contain check — an absolute path that lands inside the workdir is permitted, only paths that resolve *outside* are rejected. ``Path.resolve()`` follows every symlink (including the leaf, even a dangling one) before the containment check, so a symlink under the workdir that targets ``/etc`` is rejected — and the resolved path is what the tool then operates on, so it can't be followed afterwards either. See the trust model on :class:`AgentToolContext`. """ candidate = Path(p) if ctx.unrestricted_paths and candidate.is_absolute(): return candidate.resolve() root = Path(ctx.workdir).resolve() full = (candidate if candidate.is_absolute() else root / candidate).resolve() if not ctx.unrestricted_paths and not _within(full, root): raise ValueError(f"path {p!r} escapes workdir") return full class BashResult(NamedTuple): """Result of :meth:`BashSession.exec` — the captured output and exit code. A ``NamedTuple`` so it unpacks positionally (``out, code = await s.exec(...)``) and reads by name (``result.output`` / ``result.exit_code``) interchangeably. """ output: str """The command's combined stdout + stderr (ANSI escapes stripped, possibly truncated to the last :data:`BASH_OUTPUT_LIMIT` bytes).""" exit_code: int """The command's exit status. ``-1`` when the exit code could not be parsed from the shell sentinel (e.g. truncated output).""" class BashSession: """A persistent ``/bin/bash`` process; cwd, env and jobs survive across calls. .. warning:: :class:`BashSession` is **stateful and not safe to share concurrently**. Interleaved :meth:`exec` calls would race for the same stdin/stdout pipes (mixed input, output read by the wrong caller, and corrupted sentinel detection). Each :class:`AgentToolContext` creates its own session, so the safe pattern is *one context per session* — never a single ``AgentToolContext`` (or hand-constructed ``BashSession``) shared across multiple sessions running on different self-hosted environments. Holding the shared instance behind a per-call lock would serialize all bash work and is almost certainly not what you want. """ def __init__(self, proc: anyio.abc.Process) -> None: """Use :meth:`BashSession.start` to construct — ``__init__`` takes an already-spawned process and is intended for internal use.""" self._proc = proc @classmethod async def start(cls, workdir: str | os.PathLike[str], *, env: Optional[Mapping[str, str]] = None) -> BashSession: base = dict(env) if env is not None else _default_bash_env() proc = await anyio.open_process( ["/bin/bash", "--noprofile", "--norc"], cwd=workdir, env={**base, "PS1": "", "PS2": "", "TERM": "dumb"}, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ) return cls(proc) @property def closed(self) -> bool: """Whether the underlying bash process has exited / been torn down. Inverse of "alive". Named ``closed`` (not ``alive``) to match the TS ``BashSession.closed`` boolean — porting code between the two SDKs should not have to flip the sense of this check. """ return self._proc.returncode is not None async def exec(self, cmd: str, timeout: float = BASH_DEFAULT_TIMEOUT) -> BashResult: if self.closed: raise RuntimeError("bash session terminated; restart required") assert self._proc.stdin is not None and self._proc.stdout is not None stdin = self._proc.stdin stdout = self._proc.stdout # Per-call nonce so a command that prints a fixed marker can't spoof the # exit-code framing. The `''` split keeps the literal out of what we # write to stdin — only the shell's printf reassembles it. sentinel = f"__ANT_CMD_{uuid.uuid4().hex}_DONE__" sentinel_split = f"{sentinel[:8]}''{sentinel[8:]}" # &1; printf '\\n{sentinel_split}%d\\n' $?\n" await stdin.send(wrapped.encode()) buf = bytearray() truncated = False marker = sentinel.encode() async def read_until_sentinel() -> None: nonlocal truncated while True: try: chunk = await stdout.receive(4096) except anyio.EndOfStream: return if not chunk: return buf.extend(chunk) if len(buf) > BASH_OUTPUT_LIMIT: # Keep only the tail so the sentinel remains detectable and # the buffer cannot grow without bound. del buf[: len(buf) - BASH_OUTPUT_LIMIT] truncated = True if marker in buf: return try: with anyio.fail_after(timeout): await read_until_sentinel() except TimeoutError as e: # This call's own deadline fired. Tear down the subprocess so the # timed-out command can't bleed into the next call. Shielded so the # teardown still completes if an outer scope is also cancelling. with anyio.CancelScope(shield=True): await self.close() raise TimeoutError(f"bash command timed out after {timeout}s") from e except anyio.get_cancelled_exc_class(): # A cancellation from *any outer scope* (e.g. the session runner's # ``TOOL_TIMEOUT`` fail_after winning a race, or a worker-wide # shutdown) unwinds this call without ever raising ``TimeoutError``, # so the branch above never runs. Without closing here the # subprocess would be left alive with the in-flight command still # queued, and the NEXT exec() would read this command's stale # output + old sentinel — silent cross-call corruption. Close it # (shielded, since we're already cancelled) and re-raise the # cancellation; never swallow it. with anyio.CancelScope(shield=True): await self.close() raise text = _ANSI.sub("", buf.decode(errors="replace")) idx = text.rfind(sentinel) if idx < 0: return BashResult(text.strip(), -1) out = text[:idx].rstrip("\n") tail = text[idx + len(sentinel) :].strip() try: code = int(tail.splitlines()[0]) if tail else -1 except ValueError: code = -1 if truncated: out = "[output truncated]\n" + out return BashResult(out, code) async def close(self) -> None: if self._proc.stdin is not None: with anyio.CancelScope(shield=True): try: await self._proc.stdin.aclose() except Exception: pass if self._proc.returncode is None: self._proc.kill() with anyio.move_on_after(2): await self._proc.wait() with anyio.CancelScope(shield=True): try: await self._proc.aclose() except Exception: pass def beta_bash_tool(ctx: AgentToolContext) -> BetaAsyncFunctionTool[Any]: @asynccontextmanager async def bash_tool() -> AsyncIterator[Callable[..., Awaitable[str]]]: """Run a command in a persistent bash shell.""" # The bash tool owns its own persistent shell for the lifetime of the # tool run. Defining it as an async context manager lets the tool runner # drive this cleanup on exit, so the bash tool no longer needs # AgentToolContext purely for that lifecycle — it only reads the workdir # and subprocess env off ``ctx``. session: BashSession | None = None async def _session() -> BashSession: nonlocal session if session is None: session = await BashSession.start(ctx.workdir, env=ctx.env) return session # ``Optional[...]`` (not ``| None``) because ``@beta_async_tool`` # evaluates these annotations at runtime via pydantic, and PEP 604 union # syntax can't be ``eval``'d under Python 3.9 — our minimum version. async def bash( command: Optional[str] = None, restart: Optional[bool] = None, timeout_ms: Optional[int] = None ) -> str: nonlocal session if restart: if session is not None: await session.close() session = None await _session() return "bash session restarted" if not command: raise ToolError("bash: command is required") timeout = timeout_ms / 1000.0 if timeout_ms else BASH_DEFAULT_TIMEOUT try: s = await _session() out, code = await s.exec(command, timeout=timeout) except (RuntimeError, TimeoutError) as e: raise ToolError(f"bash: {e}") from e if code != 0: raise ToolError(out) return out try: yield bash finally: if session is not None: await session.close() # ``@beta_async_tool`` detects the async context manager, enters it lazily # on first call to obtain the ``bash`` callable, and drives its ``__aexit__`` # on the tool-runner cleanup path. The ``cast`` is only to satisfy the # decorator's "async function" overload — the runtime object is the # context-manager factory the decorator expects. return beta_async_tool( name="bash", input_schema=BetaManagedAgentsAgentToolset20260401BashInput, )(cast(Any, bash_tool)) def _read_binary_block(target: Path, file_path: str, size: int, media_type: str, ctx: AgentToolContext) -> BetaContent: """Read an image/PDF as a base64 ``image``/``document`` content block. The text cap does not apply here — its 256 KiB default would reject most real images. Instead the media caps (``max_image_base64_bytes`` / ``max_pdf_bytes``, defaulting to the API's own limits) govern, checked against the stat size before opening (same OOM rationale as the text path) and tightened by an *explicitly* configured ``max_file_bytes`` — an explicit cap is a memory bound and binds every read. """ # The image cap is on the encoded form: n raw bytes -> 4*ceil(n/3) base64. if media_type == "application/pdf": limit = _resolve_max_bytes(ctx.max_pdf_bytes, DEFAULT_MAX_PDF_BYTES) else: b64_cap = _resolve_max_bytes(ctx.max_image_base64_bytes, DEFAULT_MAX_IMAGE_BASE64_BYTES) limit = (b64_cap // 4) * 3 if b64_cap is not None else None if is_given(ctx.max_file_bytes) and ctx.max_file_bytes is not None: limit = ctx.max_file_bytes if limit is None else min(limit, ctx.max_file_bytes) if limit is not None and size > limit: raise ToolError(f"read: {file_path} is {size} bytes, exceeds {limit}-byte limit for image/PDF files.") data = base64.standard_b64encode(target.read_bytes()).decode("ascii") if media_type == "application/pdf": return {"type": "document", "source": {"type": "base64", "media_type": "application/pdf", "data": data}} return { "type": "image", "source": {"type": "base64", "media_type": cast(Any, media_type), "data": data}, } def beta_read_tool(ctx: AgentToolContext) -> BetaAsyncFunctionTool[Any]: @beta_async_tool(name="read", input_schema=BetaManagedAgentsAgentToolset20260401ReadInput) async def read(file_path: str, view_range: Optional[List[int]] = None) -> BetaFunctionToolResultType: """Read a file rooted at the working directory.""" try: target = resolve_path(ctx, file_path) except ValueError as e: raise ToolError(f"read: {e}") from e try: # stat() before any open(): the size cap stops a multi-GB file from # OOM'ing the runner, and is_file() rejects FIFOs/devices/dirs # without opening them (open() on an unconnected FIFO blocks). st = target.stat() if not S_ISREG(st.st_mode): raise ToolError(f"read: {file_path}: not a regular file") media_type = _BINARY_MEDIA_TYPES.get(target.suffix.lower()) if media_type is not None: # Images/PDFs come back as content blocks (hosted-toolset # parity) — read_text() on them raises UnicodeDecodeError. if view_range: raise ToolError("read: view_range is not supported for image/PDF files") return [_read_binary_block(target, file_path, st.st_size, media_type, ctx)] limit = _resolve_max_bytes(ctx.max_file_bytes) if limit is not None and st.st_size > limit: raise ToolError( f"read: {file_path} is {st.st_size} bytes, exceeds {limit}-byte limit. " "Use bash (head/tail/sed) to read a slice." ) # Explicit UTF-8: the locale default varies by host (ASCII under # LANG=C), which would mislabel valid UTF-8 as binary below. text = target.read_text(encoding="utf-8") except ToolError: raise except UnicodeDecodeError as e: raise ToolError( f"read: {file_path}: not valid UTF-8 text (binary files are only supported for image/PDF extensions)" ) from e except OSError as e: raise _fs_error("read", file_path, e) from e if not view_range: return text if len(view_range) != 2: raise ToolError("read: view_range must be [start_line, end_line]") start_line, end_line = view_range lines = text.split("\n") start = max(0, start_line - 1) end = end_line if end_line > 0 else len(lines) return "\n".join(lines[start:end]) return read def beta_write_tool(ctx: AgentToolContext) -> BetaAsyncFunctionTool[Any]: @beta_async_tool(name="write", input_schema=BetaManagedAgentsAgentToolset20260401WriteInput) async def write(file_path: str, content: str) -> str: """Write a file, creating parent directories as needed.""" try: target = resolve_path(ctx, file_path) except ValueError as e: raise ToolError(f"write: {e}") from e try: target.parent.mkdir(parents=True, exist_ok=True) target.write_text(content, encoding="utf-8") except OSError as e: raise _fs_error("write", file_path, e) from e return f"wrote {len(content)} bytes to {file_path}" return write def beta_edit_tool(ctx: AgentToolContext) -> BetaAsyncFunctionTool[Any]: @beta_async_tool(name="edit", input_schema=BetaManagedAgentsAgentToolset20260401EditInput) async def edit(file_path: str, old_string: str, new_string: str, replace_all: Optional[bool] = None) -> str: """Replace text in a file by exact string match.""" try: target = resolve_path(ctx, file_path) except ValueError as e: raise ToolError(f"edit: {e}") from e try: # stat() before any open(): the size cap stops a multi-GB file from # OOM'ing the runner, and is_file() rejects FIFOs/devices/dirs # without opening them (open() on an unconnected FIFO blocks). Same # guard as the read tool — edit reads the whole file too. st = target.stat() if not S_ISREG(st.st_mode): raise ToolError(f"edit: {file_path}: not a regular file") limit = _resolve_max_bytes(ctx.max_file_bytes) if limit is not None and st.st_size > limit: raise ToolError( f"edit: {file_path} is {st.st_size} bytes, exceeds {limit}-byte limit. " "Use bash (sed/awk) to edit a large file." ) text = target.read_text(encoding="utf-8") except ToolError: raise except UnicodeDecodeError as e: raise ToolError(f"edit: {file_path}: not valid UTF-8 text (cannot edit binary files)") from e except OSError as e: raise _fs_error("edit", file_path, e) from e count = text.count(old_string) if count == 0: raise ToolError(f"edit: old_string not found in {file_path}") if not replace_all and count > 1: raise ToolError(f"edit: old_string appears {count} times in {file_path} (must be unique)") updated = text.replace(old_string, new_string) if replace_all else text.replace(old_string, new_string, 1) try: target.write_text(updated, encoding="utf-8") except OSError as e: raise _fs_error("edit", file_path, e) from e return f"edited {file_path} ({count if replace_all else 1} replacement(s))" return edit def _mtime_or_zero(p: Path) -> float: try: return p.stat().st_mtime except OSError: return 0.0 def beta_glob_tool(ctx: AgentToolContext) -> BetaAsyncFunctionTool[Any]: @beta_async_tool(name="glob", input_schema=BetaManagedAgentsAgentToolset20260401GlobInput) async def glob(pattern: str, path: Optional[str] = None) -> str: """List files matching a glob pattern, newest first.""" confine: Optional[Path] = None if Path(pattern).is_absolute(): if not ctx.unrestricted_paths: raise ToolError("glob: absolute pattern not permitted") root = Path("/") pat = pattern.lstrip("/") else: # ``Path.glob`` honours literal ``..`` segments, so a pattern like # ``../../etc/*`` would escape the workdir before resolve_path() is # ever consulted — reject it up front. if not ctx.unrestricted_paths and ".." in PurePosixPath(pattern).parts: raise ToolError("glob: '..' is not permitted in the pattern") if path: try: root = resolve_path(ctx, path) except ValueError as e: raise ToolError(f"glob: {e}") from e else: root = Path(ctx.workdir).resolve() pat = pattern if not ctx.unrestricted_paths: confine = root try: # islice caps the materialised match list so a pattern that matches # an enormous tree can't OOM the runner. matches = list(islice(root.glob(pat), WALK_MAX_ENTRIES)) except (ValueError, OSError) as e: raise ToolError(f"glob: {e}") from e if confine is not None: # Post-filter: a symlink traversed mid-pattern (glob follows # symlinks for non-``**`` segments) must not let a result escape the # confinement root. ``resolve()`` canonicalises symlinks. matches = [m for m in matches if _within(m.resolve(), confine)] if not matches: return "no matches" matches.sort(key=_mtime_or_zero, reverse=True) return "\n".join(str(m) for m in matches[:GLOB_RESULT_LIMIT]) return glob def beta_grep_tool(ctx: AgentToolContext) -> BetaAsyncFunctionTool[Any]: @beta_async_tool(name="grep", input_schema=BetaManagedAgentsAgentToolset20260401GrepInput) async def grep(pattern: str, path: Optional[str] = None) -> str: """Search file contents for a regular expression.""" try: search = resolve_path(ctx, path) if path else Path(ctx.workdir).resolve() except ValueError as e: raise ToolError(f"grep: {e}") from e if rg := shutil.which("rg"): # ``check=False`` because ripgrep exits 1 on "no matches", which # isn't an error for us — we surface it as a friendly string. result = await anyio.run_process( [rg, "-n", "--no-heading", "-e", pattern, "--", str(search)], check=False, ) if result.returncode == 1: return "no matches" if result.returncode != 0: raise ToolError(f"grep: rg failed: {result.stderr.decode(errors='replace')}") out = result.stdout.decode(errors="replace") if len(out) > GREP_OUTPUT_LIMIT: out = out[:GREP_OUTPUT_LIMIT] + f"\n[output truncated at {GREP_OUTPUT_LIMIT} bytes]" return out try: rx = re.compile(pattern) except re.error as e: raise ToolError(f"grep: invalid regex: {e}") from e return _walk_grep(rx, search) return grep def _walk_grep(rx: re.Pattern[str], search: Path) -> str: hits: list[str] = [] budget = GREP_OUTPUT_LIMIT def push(line: str) -> bool: nonlocal budget budget -= len(line) + 1 if budget < 0: hits.append(f"[output truncated at {GREP_OUTPUT_LIMIT} bytes]") return False hits.append(line) return True def scan(full: Path) -> bool: try: with full.open("rb") as f: if b"\x00" in f.read(512): return True f.seek(0) for i, raw in enumerate(f, 1): # Cap line length: ``pattern`` is model-supplied and Python's # ``re`` backtracks, so a pathological pattern against a very # long line is a ReDoS. if len(raw) > GREP_MAX_LINE_LENGTH: continue line = raw.decode(errors="replace").rstrip("\r\n") if rx.search(line) and not push(f"{full}:{i}:{line}"): return False except OSError: pass return True if search.is_file(): scan(search) else: seen = 0 for dirpath, dirnames, filenames in os.walk(search): # Never descend into a symlinked directory: a symlink in the workdir # pointing at ``/`` would otherwise let grep walk straight out of it. dirnames[:] = [ d for d in dirnames if d not in (".git", "node_modules") and not (Path(dirpath) / d).is_symlink() ] for name in filenames: full = Path(dirpath) / name # Likewise skip symlinked files — a symlink to /etc/shadow must # not be read through just because it lives inside the workdir. if full.is_symlink(): continue seen += 1 if seen > WALK_MAX_ENTRIES or not scan(full): return "\n".join(hits) if hits else "no matches" return "\n".join(hits) if hits else "no matches" def beta_agent_toolset_20260401(ctx: AgentToolContext) -> list[BetaAsyncFunctionTool[Any]]: """Return the ``agent_toolset_20260401`` implementations bound to ``ctx``. The result is a plain list of :class:`~anthropic.lib.tools.BetaAsyncFunctionTool` instances — *async* function tools, so it is for the **async** runners only: the ``AsyncAnthropic`` ``client.beta.messages.tool_runner`` and ``client.beta.sessions.events.tool_runner`` (always async). The sync ``Anthropic`` ``messages.tool_runner`` takes ``BetaRunnableTool``, which excludes the async function tools this returns. Filter or extend it before passing it on:: tools = [*beta_agent_toolset_20260401(ctx), my_custom_tool] tools = [t for t in beta_agent_toolset_20260401(ctx) if t.name != "grep"] """ return [ beta_bash_tool(ctx), beta_read_tool(ctx), beta_write_tool(ctx), beta_edit_tool(ctx), beta_glob_tool(ctx), beta_grep_tool(ctx), ] anthropic-sdk-python-0.120.2/src/anthropic/lib/tools/mcp.py000066400000000000000000000406021523216435200235630ustar00rootroot00000000000000"""Helpers for integrating MCP (Model Context Protocol) SDK types with the Anthropic SDK. These helpers reduce boilerplate when converting between MCP types and Anthropic API types. Usage:: from anthropic.lib.tools.mcp import mcp_tool, async_mcp_tool, mcp_message This module requires the ``mcp`` package to be installed. """ # pyright: reportUnknownArgumentType=false, reportUnknownMemberType=false, reportUnknownVariableType=false, reportMissingImports=false, reportUnknownParameterType=false from __future__ import annotations import json import base64 from typing import Any, Iterable from urllib.parse import urlparse from typing_extensions import Literal try: from mcp.types import ( # type: ignore[import-not-found] Tool, TextContent, ContentBlock, ImageContent, PromptMessage, CallToolResult, EmbeddedResource, ReadResourceResult, BlobResourceContents, TextResourceContents, ) from mcp.client.session import ClientSession # type: ignore[import-not-found] except ImportError as _err: raise ImportError( "The `mcp` package is required to use MCP helpers. Install it with: pip install anthropic[mcp]. Requires Python 3.10 or higher." ) from _err from ...types.beta import ( BetaBase64PDFSourceParam, BetaPlainTextSourceParam, BetaBase64ImageSourceParam, BetaCacheControlEphemeralParam, ) from ._beta_functions import ( ToolError, BetaFunctionTool, BetaAsyncFunctionTool, BetaFunctionToolResultType, beta_tool, beta_async_tool, ) from .._stainless_helpers import tag_helper from ...types.beta.beta_tool_result_block_param import Content as BetaContent __all__ = [ "mcp_tool", "async_mcp_tool", "mcp_content", "mcp_message", "mcp_resource_to_content", "mcp_resource_to_file", "UnsupportedMCPValueError", ] # ----------------------------------------------------------------------- # mcp version compatibility # ----------------------------------------------------------------------- # mcp<2 exposes camelCase model fields (`tool.inputSchema`); mcp>=2 exposes # snake_case (`tool.input_schema`). Read through this helper to support both. _MCP_V1_NAMES = { "input_schema": "inputSchema", "mime_type": "mimeType", "is_error": "isError", "structured_content": "structuredContent", } def _mcp_field_v1_or_v2(obj: Any, name: str) -> Any: try: return getattr(obj, name) except AttributeError: return getattr(obj, _MCP_V1_NAMES[name]) # ----------------------------------------------------------------------- # Supported MIME types # ----------------------------------------------------------------------- _SUPPORTED_IMAGE_TYPES = frozenset({"image/jpeg", "image/png", "image/gif", "image/webp"}) class _TaggedDict(dict): # type: ignore[type-arg] """A dict subclass that can carry a ``_stainless_helper`` attribute. Behaves identically to a regular dict for serialization and isinstance checks, but allows attaching tracking metadata that won't appear in JSON output. """ class _TaggedTuple(tuple): # type: ignore[type-arg] """A tuple subclass that can carry a ``_stainless_helper`` attribute.""" def _is_supported_image_type(mime_type: str) -> bool: return mime_type in _SUPPORTED_IMAGE_TYPES def _is_supported_resource_mime_type(mime_type: str | None) -> bool: return ( mime_type is None or mime_type.startswith("text/") or mime_type == "application/pdf" or _is_supported_image_type(mime_type) ) # ----------------------------------------------------------------------- # Errors # ----------------------------------------------------------------------- class UnsupportedMCPValueError(Exception): """Raised when an MCP value cannot be converted to a format supported by the Claude API.""" # ----------------------------------------------------------------------- # Content conversion # ----------------------------------------------------------------------- def mcp_content( content: ContentBlock, *, cache_control: BetaCacheControlEphemeralParam | None = None, ) -> BetaContent: """Convert a single MCP content block to an Anthropic content block. Handles text, image, and embedded resource content types. Raises :class:`UnsupportedMCPValueError` for audio and resource_link types. """ if isinstance(content, TextContent): block = _TaggedDict({"type": "text", "text": content.text}) if cache_control is not None: block["cache_control"] = cache_control tag_helper(block, "mcp_content") return block # type: ignore[return-value] if isinstance(content, ImageContent): mime_type = _mcp_field_v1_or_v2(content, "mime_type") if not _is_supported_image_type(mime_type): raise UnsupportedMCPValueError(f"Unsupported image MIME type: {mime_type}") image_block = _TaggedDict( { "type": "image", "source": BetaBase64ImageSourceParam( type="base64", data=content.data, media_type=mime_type, # type: ignore[typeddict-item] ), } ) if cache_control is not None: image_block["cache_control"] = cache_control tag_helper(image_block, "mcp_content") return image_block # type: ignore[return-value] if isinstance(content, EmbeddedResource): return _resource_contents_to_block(content.resource, cache_control=cache_control) # audio, resource_link, or unknown content_type = getattr(content, "type", type(content).__name__) raise UnsupportedMCPValueError(f"Unsupported MCP content type: {content_type}") def _resource_contents_to_block( resource: TextResourceContents | BlobResourceContents, *, cache_control: BetaCacheControlEphemeralParam | None = None, ) -> BetaContent: """Convert MCP resource contents to an Anthropic content block.""" mime_type = _mcp_field_v1_or_v2(resource, "mime_type") # Images if mime_type is not None and _is_supported_image_type(mime_type): if not isinstance(resource, BlobResourceContents): raise UnsupportedMCPValueError(f"Image resource must have blob data, not text. URI: {resource.uri}") image_block = _TaggedDict( { "type": "image", "source": BetaBase64ImageSourceParam( type="base64", data=resource.blob, media_type=mime_type, # type: ignore[typeddict-item] ), } ) if cache_control is not None: image_block["cache_control"] = cache_control tag_helper(image_block, "mcp_resource_to_content") return image_block # type: ignore[return-value] # PDFs if mime_type == "application/pdf": if not isinstance(resource, BlobResourceContents): raise UnsupportedMCPValueError(f"PDF resource must have blob data, not text. URI: {resource.uri}") pdf_block = _TaggedDict( { "type": "document", "source": BetaBase64PDFSourceParam( type="base64", data=resource.blob, media_type="application/pdf", ), } ) if cache_control is not None: pdf_block["cache_control"] = cache_control tag_helper(pdf_block, "mcp_resource_to_content") return pdf_block # type: ignore[return-value] # Text (text/*, or no MIME type) if mime_type is None or mime_type.startswith("text/"): if isinstance(resource, TextResourceContents): data = resource.text else: data = base64.b64decode(resource.blob).decode("utf-8") text_block = _TaggedDict( { "type": "document", "source": BetaPlainTextSourceParam( type="text", data=data, media_type="text/plain", ), } ) if cache_control is not None: text_block["cache_control"] = cache_control tag_helper(text_block, "mcp_resource_to_content") return text_block # type: ignore[return-value] raise UnsupportedMCPValueError(f'Unsupported MIME type "{mime_type}" for resource: {resource.uri}') # ----------------------------------------------------------------------- # Message conversion # ----------------------------------------------------------------------- def mcp_message( message: PromptMessage, *, cache_control: BetaCacheControlEphemeralParam | None = None, ) -> dict[str, Any]: """Convert an MCP prompt message to an Anthropic ``BetaMessageParam``.""" result = _TaggedDict( { "role": message.role, "content": [mcp_content(message.content, cache_control=cache_control)], } ) tag_helper(result, "mcp_message") return result # ----------------------------------------------------------------------- # Resource conversion # ----------------------------------------------------------------------- def mcp_resource_to_content( result: ReadResourceResult, *, cache_control: BetaCacheControlEphemeralParam | None = None, ) -> BetaContent: """Convert MCP resource contents to an Anthropic content block. Finds the first resource with a supported MIME type from the result's ``contents`` list. """ if not result.contents: raise UnsupportedMCPValueError("Resource contents array must contain at least one item") mime_types = [_mcp_field_v1_or_v2(c, "mime_type") for c in result.contents] supported = next( (c for c, mime_type in zip(result.contents, mime_types) if _is_supported_resource_mime_type(mime_type)), None, ) if supported is None: mime_types = [m for m in mime_types if m is not None] raise UnsupportedMCPValueError( f"No supported MIME type found in resource contents. Available: {', '.join(mime_types)}" ) return _resource_contents_to_block(supported, cache_control=cache_control) def mcp_resource_to_file( result: ReadResourceResult, ) -> tuple[str | None, bytes, str | None]: """Convert MCP resource contents to a file tuple for ``files.upload()``. Returns a ``(filename, content_bytes, mime_type)`` tuple compatible with the SDK's ``FileTypes``. """ if not result.contents: raise UnsupportedMCPValueError("Resource contents array must contain at least one item") resource = result.contents[0] uri_str = str(resource.uri) # Extract filename from URI path = urlparse(uri_str).path name = path.rsplit("/", 1)[-1] if path else None # Get bytes if isinstance(resource, BlobResourceContents): content_bytes = base64.b64decode(resource.blob) else: content_bytes = resource.text.encode("utf-8") file_tuple = _TaggedTuple((name, content_bytes, _mcp_field_v1_or_v2(resource, "mime_type"))) tag_helper(file_tuple, "mcp_resource_to_file") return file_tuple # ----------------------------------------------------------------------- # Tool result conversion (used by tool call handlers) # ----------------------------------------------------------------------- def _convert_tool_result(result: CallToolResult) -> BetaFunctionToolResultType: """Convert MCP ``CallToolResult`` to a value suitable for returning from ``call()``.""" if _mcp_field_v1_or_v2(result, "is_error"): raise ToolError([mcp_content(item) for item in result.content]) # If content is empty but structuredContent is present, JSON-encode it structured_content = _mcp_field_v1_or_v2(result, "structured_content") if not result.content and structured_content is not None: return json.dumps(structured_content) return [mcp_content(item) for item in result.content] # ----------------------------------------------------------------------- # Public factory functions # ----------------------------------------------------------------------- def mcp_tool( tool: Tool, client: ClientSession, *, cache_control: BetaCacheControlEphemeralParam | None = None, defer_loading: bool | None = None, allowed_callers: list[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] | None = None, eager_input_streaming: bool | None = None, input_examples: Iterable[dict[str, object]] | None = None, strict: bool | None = None, ) -> BetaFunctionTool[Any]: """Convert an MCP tool to a sync runnable tool for ``tool_runner()``. Example:: from anthropic.lib.tools.mcp import mcp_tool tools_result = await mcp_client.list_tools() runner = client.beta.messages.tool_runner( model="claude-sonnet-4-20250514", max_tokens=1024, tools=[mcp_tool(t, mcp_client) for t in tools_result.tools], messages=[{"role": "user", "content": "Use the available tools"}], ) Args: tool: An MCP tool definition from ``client.list_tools()``. client: The MCP ``ClientSession`` used to call the tool. cache_control: Cache control configuration. defer_loading: If true, tool will not be included in initial system prompt. allowed_callers: Which callers may use this tool. eager_input_streaming: Enable eager input streaming for this tool. input_examples: Example inputs for the tool. strict: When true, guarantees schema validation on tool names and inputs. """ import anyio.from_thread tool_name = tool.name def call_mcp(**kwargs: Any) -> BetaFunctionToolResultType: result = anyio.from_thread.run(client.call_tool, tool_name, kwargs) return _convert_tool_result(result) result = beta_tool( call_mcp, name=tool_name, description=tool.description, input_schema=_mcp_field_v1_or_v2(tool, "input_schema"), cache_control=cache_control, defer_loading=defer_loading, allowed_callers=allowed_callers, eager_input_streaming=eager_input_streaming, input_examples=input_examples, strict=strict, ) tag_helper(result, "mcp_tool") return result def async_mcp_tool( tool: Tool, client: ClientSession, *, cache_control: BetaCacheControlEphemeralParam | None = None, defer_loading: bool | None = None, allowed_callers: list[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] | None = None, eager_input_streaming: bool | None = None, input_examples: Iterable[dict[str, object]] | None = None, strict: bool | None = None, ) -> BetaAsyncFunctionTool[Any]: """Convert an MCP tool to an async runnable tool for ``tool_runner()``. Example:: from anthropic.lib.tools.mcp import async_mcp_tool tools_result = await mcp_client.list_tools() runner = await client.beta.messages.tool_runner( model="claude-sonnet-4-20250514", max_tokens=1024, tools=[async_mcp_tool(t, mcp_client) for t in tools_result.tools], messages=[{"role": "user", "content": "Use the available tools"}], ) Args: tool: An MCP tool definition from ``client.list_tools()``. client: The MCP ``ClientSession`` used to call the tool. cache_control: Cache control configuration. defer_loading: If true, tool will not be included in initial system prompt. allowed_callers: Which callers may use this tool. eager_input_streaming: Enable eager input streaming for this tool. input_examples: Example inputs for the tool. strict: When true, guarantees schema validation on tool names and inputs. """ tool_name = tool.name async def call_mcp(**kwargs: Any) -> BetaFunctionToolResultType: result = await client.call_tool(name=tool_name, arguments=kwargs) return _convert_tool_result(result) result = beta_async_tool( call_mcp, name=tool_name, description=tool.description, input_schema=_mcp_field_v1_or_v2(tool, "input_schema"), cache_control=cache_control, defer_loading=defer_loading, allowed_callers=allowed_callers, eager_input_streaming=eager_input_streaming, input_examples=input_examples, strict=strict, ) tag_helper(result, "mcp_tool") return result anthropic-sdk-python-0.120.2/src/anthropic/lib/vertex/000077500000000000000000000000001523216435200226055ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/lib/vertex/__init__.py000066400000000000000000000001461523216435200247170ustar00rootroot00000000000000from ._client import AnthropicVertex as AnthropicVertex, AsyncAnthropicVertex as AsyncAnthropicVertex anthropic-sdk-python-0.120.2/src/anthropic/lib/vertex/_auth.py000066400000000000000000000014721523216435200242630ustar00rootroot00000000000000from __future__ import annotations from typing import TYPE_CHECKING from .._extras._google_auth import refresh_credentials as refresh_auth, load_default_credentials if TYPE_CHECKING: from google.auth.credentials import Credentials # type: ignore[import-untyped] # Note: these functions are blocking as they make HTTP requests, the async # client runs these functions in a separate thread to ensure they do not # cause synchronous blocking issues. __all__ = ["load_auth", "refresh_auth"] def load_auth(*, project_id: str | None) -> tuple[Credentials, str]: credentials, loaded_project_id = load_default_credentials(extra="vertex") if not project_id: project_id = loaded_project_id if not project_id: raise ValueError("Could not resolve project_id") return credentials, project_id anthropic-sdk-python-0.120.2/src/anthropic/lib/vertex/_beta.py000066400000000000000000000063731523216435200242420ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ._beta_messages import ( Messages, AsyncMessages, MessagesWithRawResponse, AsyncMessagesWithRawResponse, MessagesWithStreamingResponse, AsyncMessagesWithStreamingResponse, ) __all__ = ["Beta", "AsyncBeta"] class Beta(SyncAPIResource): @cached_property def messages(self) -> Messages: return Messages(self._client) @cached_property def with_raw_response(self) -> BetaWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return BetaWithRawResponse(self) @cached_property def with_streaming_response(self) -> BetaWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return BetaWithStreamingResponse(self) class AsyncBeta(AsyncAPIResource): @cached_property def messages(self) -> AsyncMessages: return AsyncMessages(self._client) @cached_property def with_raw_response(self) -> AsyncBetaWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncBetaWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncBetaWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncBetaWithStreamingResponse(self) class BetaWithRawResponse: def __init__(self, beta: Beta) -> None: self._beta = beta @cached_property def messages(self) -> MessagesWithRawResponse: return MessagesWithRawResponse(self._beta.messages) class AsyncBetaWithRawResponse: def __init__(self, beta: AsyncBeta) -> None: self._beta = beta @cached_property def messages(self) -> AsyncMessagesWithRawResponse: return AsyncMessagesWithRawResponse(self._beta.messages) class BetaWithStreamingResponse: def __init__(self, beta: Beta) -> None: self._beta = beta @cached_property def messages(self) -> MessagesWithStreamingResponse: return MessagesWithStreamingResponse(self._beta.messages) class AsyncBetaWithStreamingResponse: def __init__(self, beta: AsyncBeta) -> None: self._beta = beta @cached_property def messages(self) -> AsyncMessagesWithStreamingResponse: return AsyncMessagesWithStreamingResponse(self._beta.messages) anthropic-sdk-python-0.120.2/src/anthropic/lib/vertex/_beta_messages.py000066400000000000000000000065071523216435200261300ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from ... import _legacy_response from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ...resources.beta import Messages as FirstPartyMessagesAPI, AsyncMessages as FirstPartyAsyncMessagesAPI __all__ = ["Messages", "AsyncMessages"] class Messages(SyncAPIResource): create = FirstPartyMessagesAPI.create stream = FirstPartyMessagesAPI.stream count_tokens = FirstPartyMessagesAPI.count_tokens @cached_property def with_raw_response(self) -> MessagesWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return MessagesWithRawResponse(self) @cached_property def with_streaming_response(self) -> MessagesWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return MessagesWithStreamingResponse(self) class AsyncMessages(AsyncAPIResource): create = FirstPartyAsyncMessagesAPI.create stream = FirstPartyAsyncMessagesAPI.stream count_tokens = FirstPartyAsyncMessagesAPI.count_tokens @cached_property def with_raw_response(self) -> AsyncMessagesWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncMessagesWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncMessagesWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncMessagesWithStreamingResponse(self) class MessagesWithRawResponse: def __init__(self, messages: Messages) -> None: self._messages = messages self.create = _legacy_response.to_raw_response_wrapper( messages.create, ) class AsyncMessagesWithRawResponse: def __init__(self, messages: AsyncMessages) -> None: self._messages = messages self.create = _legacy_response.async_to_raw_response_wrapper( messages.create, ) class MessagesWithStreamingResponse: def __init__(self, messages: Messages) -> None: self._messages = messages self.create = to_streamed_response_wrapper( messages.create, ) class AsyncMessagesWithStreamingResponse: def __init__(self, messages: AsyncMessages) -> None: self._messages = messages self.create = async_to_streamed_response_wrapper( messages.create, ) anthropic-sdk-python-0.120.2/src/anthropic/lib/vertex/_client.py000066400000000000000000000441231523216435200246000ustar00rootroot00000000000000from __future__ import annotations import os from typing import TYPE_CHECKING, Any, Union, Mapping, TypeVar, Sequence from typing_extensions import Self, override import httpx from ... import _exceptions from ._auth import load_auth, refresh_auth from ._beta import Beta, AsyncBeta from ..._types import NOT_GIVEN, NotGiven from ..._utils import is_dict, asyncify, is_given from ..._compat import model_copy, typed_cached_property from ..._models import FinalRequestOptions from ..._version import __version__ from ..._streaming import Stream, AsyncStream from ..._exceptions import AnthropicError, APIStatusError from ..._middleware import MiddlewareInput from ..._base_client import ( DEFAULT_MAX_RETRIES, BaseClient, SyncAPIClient, AsyncAPIClient, merge_headers, ) from ...resources.messages import Messages, AsyncMessages if TYPE_CHECKING: from google.auth.credentials import Credentials as GoogleCredentials # type: ignore DEFAULT_VERSION = "vertex-2023-10-16" _HttpxClientT = TypeVar("_HttpxClientT", bound=Union[httpx.Client, httpx.AsyncClient]) _DefaultStreamT = TypeVar("_DefaultStreamT", bound=Union[Stream[Any], AsyncStream[Any]]) class BaseVertexClient(BaseClient[_HttpxClientT, _DefaultStreamT]): @typed_cached_property def region(self) -> str: raise RuntimeError("region not set") @typed_cached_property def project_id(self) -> str | None: project_id = os.environ.get("ANTHROPIC_VERTEX_PROJECT_ID") if project_id: return project_id return None @override def _make_status_error( self, err_msg: str, *, body: object, response: httpx.Response, ) -> APIStatusError: if response.status_code == 400: return _exceptions.BadRequestError(err_msg, response=response, body=body) if response.status_code == 401: return _exceptions.AuthenticationError(err_msg, response=response, body=body) if response.status_code == 403: return _exceptions.PermissionDeniedError(err_msg, response=response, body=body) if response.status_code == 404: return _exceptions.NotFoundError(err_msg, response=response, body=body) if response.status_code == 409: return _exceptions.ConflictError(err_msg, response=response, body=body) if response.status_code == 422: return _exceptions.UnprocessableEntityError(err_msg, response=response, body=body) if response.status_code == 429: return _exceptions.RateLimitError(err_msg, response=response, body=body) if response.status_code == 503: return _exceptions.ServiceUnavailableError(err_msg, response=response, body=body) if response.status_code == 504: return _exceptions.DeadlineExceededError(err_msg, response=response, body=body) if response.status_code >= 500: return _exceptions.InternalServerError(err_msg, response=response, body=body) return APIStatusError(err_msg, response=response, body=body) class AnthropicVertex(BaseVertexClient[httpx.Client, Stream[Any]], SyncAPIClient): messages: Messages beta: Beta def __init__( self, *, region: str | NotGiven = NOT_GIVEN, project_id: str | NotGiven = NOT_GIVEN, access_token: str | None = None, credentials: GoogleCredentials | None = None, base_url: str | httpx.URL | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, # Configure a custom httpx client. See the [httpx documentation](https://www.python-httpx.org/api/#client) for more details. http_client: httpx.Client | None = None, middleware: Sequence[MiddlewareInput] | None = None, _strict_response_validation: bool = False, ) -> None: if not is_given(region): region = os.environ.get("CLOUD_ML_REGION", NOT_GIVEN) if not is_given(region): raise ValueError( "No region was given. The client should be instantiated with the `region` argument or the `CLOUD_ML_REGION` environment variable should be set." ) if base_url is None: base_url = os.environ.get("ANTHROPIC_VERTEX_BASE_URL") if base_url is None: if region == "global": base_url = "https://aiplatform.googleapis.com/v1" elif region == "us": base_url = "https://aiplatform.us.rep.googleapis.com/v1" elif region == "eu": base_url = "https://aiplatform.eu.rep.googleapis.com/v1" else: base_url = f"https://{region}-aiplatform.googleapis.com/v1" super().__init__( version=__version__, base_url=base_url, timeout=timeout, max_retries=max_retries, custom_headers=default_headers, custom_query=default_query, http_client=http_client, middleware=middleware, _strict_response_validation=_strict_response_validation, ) if is_given(project_id): self.project_id = project_id self.region = region self.access_token = access_token self.credentials = credentials self.messages = Messages(self) self.beta = Beta(self) @override def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: return _prepare_options(options, project_id=self.project_id, region=self.region) @override def _prepare_request(self, request: httpx.Request) -> None: if request.headers.get("Authorization"): # already authenticated, nothing for us to do return request.headers["Authorization"] = f"Bearer {self._ensure_access_token()}" def _ensure_access_token(self) -> str: if self.access_token is not None: return self.access_token if not self.credentials: self.credentials, project_id = load_auth(project_id=self.project_id) if not self.project_id: self.project_id = project_id if self.credentials.expired or not self.credentials.token: refresh_auth(self.credentials) if not self.credentials.token: raise RuntimeError("Could not resolve API token from the environment") assert isinstance(self.credentials.token, str) return self.credentials.token def copy( self, *, region: str | NotGiven = NOT_GIVEN, project_id: str | NotGiven = NOT_GIVEN, access_token: str | None = None, credentials: GoogleCredentials | None = None, base_url: str | httpx.URL | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, http_client: httpx.Client | None = None, max_retries: int | NotGiven = NOT_GIVEN, default_headers: Mapping[str, str] | None = None, set_default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, set_default_query: Mapping[str, object] | None = None, middleware: Sequence[MiddlewareInput] | None | NotGiven = NOT_GIVEN, _extra_kwargs: Mapping[str, Any] = {}, ) -> Self: """ Create a new client instance re-using the same options given to the current client with optional overriding. """ if default_headers is not None and set_default_headers is not None: raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") if default_query is not None and set_default_query is not None: raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") headers = self._custom_headers if default_headers is not None: headers = merge_headers(headers, default_headers) elif set_default_headers is not None: headers = set_default_headers params = self._custom_query if default_query is not None: params = {**params, **default_query} elif set_default_query is not None: params = set_default_query http_client = http_client or self._client return self.__class__( region=region if is_given(region) else self.region, project_id=project_id if is_given(project_id) else self.project_id or NOT_GIVEN, access_token=access_token or self.access_token, credentials=credentials or self.credentials, base_url=base_url or self.base_url, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client, max_retries=max_retries if is_given(max_retries) else self.max_retries, default_headers=headers, default_query=params, middleware=self._middleware if isinstance(middleware, NotGiven) else middleware, **_extra_kwargs, ) # Alias for `copy` for nicer inline usage, e.g. # client.with_options(timeout=10).foo.create(...) with_options = copy def with_middleware(self, *middleware: MiddlewareInput) -> Self: """A new client with the given middleware appended after this client's middleware. Convenience for applying extra middleware to a single request: ```py client.with_middleware(my_middleware).messages.create(...) ``` """ return self.copy(middleware=[*self._middleware, *middleware]) class AsyncAnthropicVertex(BaseVertexClient[httpx.AsyncClient, AsyncStream[Any]], AsyncAPIClient): messages: AsyncMessages beta: AsyncBeta def __init__( self, *, region: str | NotGiven = NOT_GIVEN, project_id: str | NotGiven = NOT_GIVEN, access_token: str | None = None, credentials: GoogleCredentials | None = None, base_url: str | httpx.URL | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, # Configure a custom httpx client. See the [httpx documentation](https://www.python-httpx.org/api/#client) for more details. http_client: httpx.AsyncClient | None = None, middleware: Sequence[MiddlewareInput] | None = None, _strict_response_validation: bool = False, ) -> None: if not is_given(region): region = os.environ.get("CLOUD_ML_REGION", NOT_GIVEN) if not is_given(region): raise ValueError( "No region was given. The client should be instantiated with the `region` argument or the `CLOUD_ML_REGION` environment variable should be set." ) if base_url is None: base_url = os.environ.get("ANTHROPIC_VERTEX_BASE_URL") if base_url is None: if region == "global": base_url = "https://aiplatform.googleapis.com/v1" elif region == "us": base_url = "https://aiplatform.us.rep.googleapis.com/v1" elif region == "eu": base_url = "https://aiplatform.eu.rep.googleapis.com/v1" else: base_url = f"https://{region}-aiplatform.googleapis.com/v1" super().__init__( version=__version__, base_url=base_url, timeout=timeout, max_retries=max_retries, custom_headers=default_headers, custom_query=default_query, http_client=http_client, middleware=middleware, _strict_response_validation=_strict_response_validation, ) if is_given(project_id): self.project_id = project_id self.region = region self.access_token = access_token self.credentials = credentials self.messages = AsyncMessages(self) self.beta = AsyncBeta(self) @override async def _prepare_options(self, options: FinalRequestOptions) -> FinalRequestOptions: return _prepare_options(options, project_id=self.project_id, region=self.region) @override async def _prepare_request(self, request: httpx.Request) -> None: if request.headers.get("Authorization"): # already authenticated, nothing for us to do return request.headers["Authorization"] = f"Bearer {await self._ensure_access_token()}" async def _ensure_access_token(self) -> str: if self.access_token is not None: return self.access_token if not self.credentials: self.credentials, project_id = await asyncify(load_auth)(project_id=self.project_id) if not self.project_id: self.project_id = project_id if self.credentials.expired or not self.credentials.token: await asyncify(refresh_auth)(self.credentials) if not self.credentials.token: raise RuntimeError("Could not resolve API token from the environment") assert isinstance(self.credentials.token, str) return self.credentials.token def copy( self, *, region: str | NotGiven = NOT_GIVEN, project_id: str | NotGiven = NOT_GIVEN, access_token: str | None = None, credentials: GoogleCredentials | None = None, base_url: str | httpx.URL | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, http_client: httpx.AsyncClient | None = None, max_retries: int | NotGiven = NOT_GIVEN, default_headers: Mapping[str, str] | None = None, set_default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, set_default_query: Mapping[str, object] | None = None, middleware: Sequence[MiddlewareInput] | None | NotGiven = NOT_GIVEN, _extra_kwargs: Mapping[str, Any] = {}, ) -> Self: """ Create a new client instance re-using the same options given to the current client with optional overriding. """ if default_headers is not None and set_default_headers is not None: raise ValueError("The `default_headers` and `set_default_headers` arguments are mutually exclusive") if default_query is not None and set_default_query is not None: raise ValueError("The `default_query` and `set_default_query` arguments are mutually exclusive") headers = self._custom_headers if default_headers is not None: headers = merge_headers(headers, default_headers) elif set_default_headers is not None: headers = set_default_headers params = self._custom_query if default_query is not None: params = {**params, **default_query} elif set_default_query is not None: params = set_default_query http_client = http_client or self._client return self.__class__( region=region if is_given(region) else self.region, project_id=project_id if is_given(project_id) else self.project_id or NOT_GIVEN, access_token=access_token or self.access_token, credentials=credentials or self.credentials, base_url=base_url or self.base_url, timeout=self.timeout if isinstance(timeout, NotGiven) else timeout, http_client=http_client, max_retries=max_retries if is_given(max_retries) else self.max_retries, default_headers=headers, default_query=params, middleware=self._middleware if isinstance(middleware, NotGiven) else middleware, **_extra_kwargs, ) # Alias for `copy` for nicer inline usage, e.g. # client.with_options(timeout=10).foo.create(...) with_options = copy def with_middleware(self, *middleware: MiddlewareInput) -> Self: """A new client with the given middleware appended after this client's middleware. Convenience for applying extra middleware to a single request: ```py client.with_middleware(my_middleware).messages.create(...) ``` """ return self.copy(middleware=[*self._middleware, *middleware]) def _prepare_options(input_options: FinalRequestOptions, *, project_id: str | None, region: str) -> FinalRequestOptions: options = model_copy(input_options, deep=True) if is_dict(options.json_data): options.json_data.setdefault("anthropic_version", DEFAULT_VERSION) if options.url in {"/v1/messages", "/v1/messages?beta=true"} and options.method == "post": if project_id is None: raise RuntimeError( "No project_id was given and it could not be resolved from credentials. The client should be instantiated with the `project_id` argument or the `ANTHROPIC_VERTEX_PROJECT_ID` environment variable should be set." ) if not is_dict(options.json_data): raise RuntimeError("Expected json data to be a dictionary for post /v1/messages") model = options.json_data.pop("model") stream = options.json_data.get("stream", False) specifier = "streamRawPredict" if stream else "rawPredict" options.url = f"/projects/{project_id}/locations/{region}/publishers/anthropic/models/{model}:{specifier}" if options.url in {"/v1/messages/count_tokens", "/v1/messages/count_tokens?beta=true"} and options.method == "post": if project_id is None: raise RuntimeError( "No project_id was given and it could not be resolved from credentials. The client should be instantiated with the `project_id` argument or the `ANTHROPIC_VERTEX_PROJECT_ID` environment variable should be set." ) options.url = f"/projects/{project_id}/locations/{region}/publishers/anthropic/models/count-tokens:rawPredict" if options.url.startswith("/v1/messages/batches"): raise AnthropicError("The Batch API is not supported in the Vertex client yet") return options anthropic-sdk-python-0.120.2/src/anthropic/pagination.py000066400000000000000000000132721523216435200232320ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Generic, TypeVar, Optional from typing_extensions import override from ._base_client import BasePage, PageInfo, BaseSyncPage, BaseAsyncPage __all__ = [ "SyncPage", "AsyncPage", "SyncTokenPage", "AsyncTokenPage", "SyncPageCursor", "AsyncPageCursor", "SyncBidirectionalPageCursor", "AsyncBidirectionalPageCursor", ] _T = TypeVar("_T") class SyncPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]): data: List[_T] has_more: Optional[bool] = None first_id: Optional[str] = None last_id: Optional[str] = None @override def _get_page_items(self) -> List[_T]: data = self.data if not data: return [] return data @override def has_next_page(self) -> bool: has_more = self.has_more if has_more is not None and has_more is False: return False return super().has_next_page() @override def next_page_info(self) -> Optional[PageInfo]: if self._options.params.get("before_id"): first_id = self.first_id if not first_id: return None return PageInfo(params={"before_id": first_id}) last_id = self.last_id if not last_id: return None return PageInfo(params={"after_id": last_id}) class AsyncPage(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): data: List[_T] has_more: Optional[bool] = None first_id: Optional[str] = None last_id: Optional[str] = None @override def _get_page_items(self) -> List[_T]: data = self.data if not data: return [] return data @override def has_next_page(self) -> bool: has_more = self.has_more if has_more is not None and has_more is False: return False return super().has_next_page() @override def next_page_info(self) -> Optional[PageInfo]: if self._options.params.get("before_id"): first_id = self.first_id if not first_id: return None return PageInfo(params={"before_id": first_id}) last_id = self.last_id if not last_id: return None return PageInfo(params={"after_id": last_id}) class SyncTokenPage(BaseSyncPage[_T], BasePage[_T], Generic[_T]): data: List[_T] has_more: Optional[bool] = None next_page: Optional[str] = None @override def _get_page_items(self) -> List[_T]: data = self.data if not data: return [] return data @override def has_next_page(self) -> bool: has_more = self.has_more if has_more is not None and has_more is False: return False return super().has_next_page() @override def next_page_info(self) -> Optional[PageInfo]: next_page = self.next_page if not next_page: return None return PageInfo(params={"page_token": next_page}) class AsyncTokenPage(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): data: List[_T] has_more: Optional[bool] = None next_page: Optional[str] = None @override def _get_page_items(self) -> List[_T]: data = self.data if not data: return [] return data @override def has_next_page(self) -> bool: has_more = self.has_more if has_more is not None and has_more is False: return False return super().has_next_page() @override def next_page_info(self) -> Optional[PageInfo]: next_page = self.next_page if not next_page: return None return PageInfo(params={"page_token": next_page}) class SyncPageCursor(BaseSyncPage[_T], BasePage[_T], Generic[_T]): data: List[_T] next_page: Optional[str] = None @override def _get_page_items(self) -> List[_T]: data = self.data if not data: return [] return data @override def next_page_info(self) -> Optional[PageInfo]: next_page = self.next_page if not next_page: return None return PageInfo(params={"page": next_page}) class AsyncPageCursor(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): data: List[_T] next_page: Optional[str] = None @override def _get_page_items(self) -> List[_T]: data = self.data if not data: return [] return data @override def next_page_info(self) -> Optional[PageInfo]: next_page = self.next_page if not next_page: return None return PageInfo(params={"page": next_page}) class SyncBidirectionalPageCursor(BaseSyncPage[_T], BasePage[_T], Generic[_T]): data: List[_T] next_page: Optional[str] = None prev_page: Optional[str] = None @override def _get_page_items(self) -> List[_T]: data = self.data if not data: return [] return data @override def next_page_info(self) -> Optional[PageInfo]: next_page = self.next_page if not next_page: return None return PageInfo(params={"page": next_page}) class AsyncBidirectionalPageCursor(BaseAsyncPage[_T], BasePage[_T], Generic[_T]): data: List[_T] next_page: Optional[str] = None prev_page: Optional[str] = None @override def _get_page_items(self) -> List[_T]: data = self.data if not data: return [] return data @override def next_page_info(self) -> Optional[PageInfo]: next_page = self.next_page if not next_page: return None return PageInfo(params={"page": next_page}) anthropic-sdk-python-0.120.2/src/anthropic/py.typed000066400000000000000000000000001523216435200222070ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/resources/000077500000000000000000000000001523216435200225345ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/resources/__init__.py000066400000000000000000000030571523216435200246520ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .beta import ( Beta, AsyncBeta, BetaWithRawResponse, AsyncBetaWithRawResponse, BetaWithStreamingResponse, AsyncBetaWithStreamingResponse, ) from .models import ( Models, AsyncModels, ModelsWithRawResponse, AsyncModelsWithRawResponse, ModelsWithStreamingResponse, AsyncModelsWithStreamingResponse, ) from .messages import ( Messages, AsyncMessages, MessagesWithRawResponse, AsyncMessagesWithRawResponse, MessagesWithStreamingResponse, AsyncMessagesWithStreamingResponse, ) from .completions import ( Completions, AsyncCompletions, CompletionsWithRawResponse, AsyncCompletionsWithRawResponse, CompletionsWithStreamingResponse, AsyncCompletionsWithStreamingResponse, ) __all__ = [ "Completions", "AsyncCompletions", "CompletionsWithRawResponse", "AsyncCompletionsWithRawResponse", "CompletionsWithStreamingResponse", "AsyncCompletionsWithStreamingResponse", "Messages", "AsyncMessages", "MessagesWithRawResponse", "AsyncMessagesWithRawResponse", "MessagesWithStreamingResponse", "AsyncMessagesWithStreamingResponse", "Models", "AsyncModels", "ModelsWithRawResponse", "AsyncModelsWithRawResponse", "ModelsWithStreamingResponse", "AsyncModelsWithStreamingResponse", "Beta", "AsyncBeta", "BetaWithRawResponse", "AsyncBetaWithRawResponse", "BetaWithStreamingResponse", "AsyncBetaWithStreamingResponse", ] anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/000077500000000000000000000000001523216435200234475ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/__init__.py000066400000000000000000000134471523216435200255710ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .beta import ( Beta, AsyncBeta, BetaWithRawResponse, AsyncBetaWithRawResponse, BetaWithStreamingResponse, AsyncBetaWithStreamingResponse, ) from .files import ( Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse, FilesWithStreamingResponse, AsyncFilesWithStreamingResponse, ) from .agents import ( Agents, AsyncAgents, AgentsWithRawResponse, AsyncAgentsWithRawResponse, AgentsWithStreamingResponse, AsyncAgentsWithStreamingResponse, ) from .dreams import ( Dreams, AsyncDreams, DreamsWithRawResponse, AsyncDreamsWithRawResponse, DreamsWithStreamingResponse, AsyncDreamsWithStreamingResponse, ) from .models import ( Models, AsyncModels, ModelsWithRawResponse, AsyncModelsWithRawResponse, ModelsWithStreamingResponse, AsyncModelsWithStreamingResponse, ) from .skills import ( Skills, AsyncSkills, SkillsWithRawResponse, AsyncSkillsWithRawResponse, SkillsWithStreamingResponse, AsyncSkillsWithStreamingResponse, ) from .vaults import ( Vaults, AsyncVaults, VaultsWithRawResponse, AsyncVaultsWithRawResponse, VaultsWithStreamingResponse, AsyncVaultsWithStreamingResponse, ) from .tunnels import ( Tunnels, AsyncTunnels, TunnelsWithRawResponse, AsyncTunnelsWithRawResponse, TunnelsWithStreamingResponse, AsyncTunnelsWithStreamingResponse, ) from .messages import ( Messages, AsyncMessages, MessagesWithRawResponse, AsyncMessagesWithRawResponse, MessagesWithStreamingResponse, AsyncMessagesWithStreamingResponse, ) from .sessions import ( Sessions, AsyncSessions, SessionsWithRawResponse, AsyncSessionsWithRawResponse, SessionsWithStreamingResponse, AsyncSessionsWithStreamingResponse, ) from .webhooks import Webhooks, AsyncWebhooks from .deployments import ( Deployments, AsyncDeployments, DeploymentsWithRawResponse, AsyncDeploymentsWithRawResponse, DeploymentsWithStreamingResponse, AsyncDeploymentsWithStreamingResponse, ) from .environments import ( Environments, AsyncEnvironments, EnvironmentsWithRawResponse, AsyncEnvironmentsWithRawResponse, EnvironmentsWithStreamingResponse, AsyncEnvironmentsWithStreamingResponse, ) from .memory_stores import ( MemoryStores, AsyncMemoryStores, MemoryStoresWithRawResponse, AsyncMemoryStoresWithRawResponse, MemoryStoresWithStreamingResponse, AsyncMemoryStoresWithStreamingResponse, ) from .user_profiles import ( UserProfiles, AsyncUserProfiles, UserProfilesWithRawResponse, AsyncUserProfilesWithRawResponse, UserProfilesWithStreamingResponse, AsyncUserProfilesWithStreamingResponse, ) from .deployment_runs import ( DeploymentRuns, AsyncDeploymentRuns, DeploymentRunsWithRawResponse, AsyncDeploymentRunsWithRawResponse, DeploymentRunsWithStreamingResponse, AsyncDeploymentRunsWithStreamingResponse, ) __all__ = [ "Models", "AsyncModels", "ModelsWithRawResponse", "AsyncModelsWithRawResponse", "ModelsWithStreamingResponse", "AsyncModelsWithStreamingResponse", "Messages", "AsyncMessages", "MessagesWithRawResponse", "AsyncMessagesWithRawResponse", "MessagesWithStreamingResponse", "AsyncMessagesWithStreamingResponse", "Agents", "AsyncAgents", "AgentsWithRawResponse", "AsyncAgentsWithRawResponse", "AgentsWithStreamingResponse", "AsyncAgentsWithStreamingResponse", "Environments", "AsyncEnvironments", "EnvironmentsWithRawResponse", "AsyncEnvironmentsWithRawResponse", "EnvironmentsWithStreamingResponse", "AsyncEnvironmentsWithStreamingResponse", "Sessions", "AsyncSessions", "SessionsWithRawResponse", "AsyncSessionsWithRawResponse", "SessionsWithStreamingResponse", "AsyncSessionsWithStreamingResponse", "Deployments", "AsyncDeployments", "DeploymentsWithRawResponse", "AsyncDeploymentsWithRawResponse", "DeploymentsWithStreamingResponse", "AsyncDeploymentsWithStreamingResponse", "DeploymentRuns", "AsyncDeploymentRuns", "DeploymentRunsWithRawResponse", "AsyncDeploymentRunsWithRawResponse", "DeploymentRunsWithStreamingResponse", "AsyncDeploymentRunsWithStreamingResponse", "Vaults", "AsyncVaults", "VaultsWithRawResponse", "AsyncVaultsWithRawResponse", "VaultsWithStreamingResponse", "AsyncVaultsWithStreamingResponse", "MemoryStores", "AsyncMemoryStores", "MemoryStoresWithRawResponse", "AsyncMemoryStoresWithRawResponse", "MemoryStoresWithStreamingResponse", "AsyncMemoryStoresWithStreamingResponse", "Files", "AsyncFiles", "FilesWithRawResponse", "AsyncFilesWithRawResponse", "FilesWithStreamingResponse", "AsyncFilesWithStreamingResponse", "Skills", "AsyncSkills", "SkillsWithRawResponse", "AsyncSkillsWithRawResponse", "SkillsWithStreamingResponse", "AsyncSkillsWithStreamingResponse", "Webhooks", "AsyncWebhooks", "UserProfiles", "AsyncUserProfiles", "UserProfilesWithRawResponse", "AsyncUserProfilesWithRawResponse", "UserProfilesWithStreamingResponse", "AsyncUserProfilesWithStreamingResponse", "Dreams", "AsyncDreams", "DreamsWithRawResponse", "AsyncDreamsWithRawResponse", "DreamsWithStreamingResponse", "AsyncDreamsWithStreamingResponse", "Tunnels", "AsyncTunnels", "TunnelsWithRawResponse", "AsyncTunnelsWithRawResponse", "TunnelsWithStreamingResponse", "AsyncTunnelsWithStreamingResponse", "Beta", "AsyncBeta", "BetaWithRawResponse", "AsyncBetaWithRawResponse", "BetaWithStreamingResponse", "AsyncBetaWithStreamingResponse", ] anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/agents/000077500000000000000000000000001523216435200247305ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/agents/__init__.py000066400000000000000000000015041523216435200270410ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .agents import ( Agents, AsyncAgents, AgentsWithRawResponse, AsyncAgentsWithRawResponse, AgentsWithStreamingResponse, AsyncAgentsWithStreamingResponse, ) from .versions import ( Versions, AsyncVersions, VersionsWithRawResponse, AsyncVersionsWithRawResponse, VersionsWithStreamingResponse, AsyncVersionsWithStreamingResponse, ) __all__ = [ "Versions", "AsyncVersions", "VersionsWithRawResponse", "AsyncVersionsWithRawResponse", "VersionsWithStreamingResponse", "AsyncVersionsWithStreamingResponse", "Agents", "AsyncAgents", "AgentsWithRawResponse", "AsyncAgentsWithRawResponse", "AgentsWithStreamingResponse", "AsyncAgentsWithStreamingResponse", ] anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/agents/agents.py000066400000000000000000001137631523216435200265760ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Union, Iterable, Optional from datetime import datetime from itertools import chain import httpx from .... import _legacy_response from .versions import ( Versions, AsyncVersions, VersionsWithRawResponse, AsyncVersionsWithRawResponse, VersionsWithStreamingResponse, AsyncVersionsWithStreamingResponse, ) from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ...._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ....pagination import SyncPageCursor, AsyncPageCursor from ....types.beta import ( BetaManagedAgentsMultiagentParams, agent_list_params, agent_create_params, agent_update_params, agent_retrieve_params, ) from ...._base_client import AsyncPaginator, make_request_options from ....types.anthropic_beta_param import AnthropicBetaParam from ....types.beta.beta_managed_agents_agent import BetaManagedAgentsAgent from ....types.beta.beta_managed_agents_skill_params import BetaManagedAgentsSkillParams from ....types.beta.beta_managed_agents_multiagent_params import BetaManagedAgentsMultiagentParams from ....types.beta.beta_managed_agents_url_mcp_server_params import BetaManagedAgentsURLMCPServerParams __all__ = ["Agents", "AsyncAgents"] class Agents(SyncAPIResource): @cached_property def versions(self) -> Versions: return Versions(self._client) @cached_property def with_raw_response(self) -> AgentsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AgentsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AgentsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AgentsWithStreamingResponse(self) def create( self, *, model: agent_create_params.Model, name: str, description: Optional[str] | Omit = omit, mcp_servers: Iterable[BetaManagedAgentsURLMCPServerParams] | Omit = omit, metadata: Dict[str, str] | Omit = omit, multiagent: Optional[BetaManagedAgentsMultiagentParams] | Omit = omit, skills: Iterable[BetaManagedAgentsSkillParams] | Omit = omit, system: Optional[str] | Omit = omit, tools: Iterable[agent_create_params.Tool] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsAgent: """Create Agent Args: model: Model identifier. Accepts the [model string](https://platform.claude.com/docs/en/about-claude/models/overview#latest-models-comparison), e.g. `claude-opus-4-6`, or a `model_config` object for additional configuration control name: Human-readable name for the agent. description: Description of what the agent does. mcp_servers: MCP servers this agent connects to. Maximum 20. Names must be unique within the array. Every server must be referenced by an `mcp_toolset` in `tools`; unreferenced servers are rejected. See the [MCP connector guide](https://platform.claude.com/docs/en/managed-agents/mcp-connector). metadata: Arbitrary key-value metadata. Maximum 16 pairs, keys up to 64 chars, values up to 512 chars. multiagent: A coordinator topology: the session's primary thread orchestrates work by spawning session threads, each running an agent drawn from the `agents` roster. skills: Skills available to the agent. system: System prompt for the agent. tools: Tool configurations available to the agent. Maximum of 128 tools across all toolsets allowed. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._post( "/v1/agents?beta=true", body=maybe_transform( { "model": model, "name": name, "description": description, "mcp_servers": mcp_servers, "metadata": metadata, "multiagent": multiagent, "skills": skills, "system": system, "tools": tools, }, agent_create_params.AgentCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsAgent, ) def retrieve( self, agent_id: str, *, version: int | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsAgent: """Get Agent Args: version: Agent version. Omit for the most recent version. Must be at least 1 if specified. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get( path_template("/v1/agents/{agent_id}?beta=true", agent_id=agent_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform({"version": version}, agent_retrieve_params.AgentRetrieveParams), ), cast_to=BetaManagedAgentsAgent, ) def update( self, agent_id: str, *, description: Optional[str] | Omit = omit, mcp_servers: Optional[Iterable[BetaManagedAgentsURLMCPServerParams]] | Omit = omit, metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, model: agent_update_params.Model | Omit = omit, multiagent: Optional[BetaManagedAgentsMultiagentParams] | Omit = omit, name: str | Omit = omit, skills: Optional[Iterable[BetaManagedAgentsSkillParams]] | Omit = omit, system: Optional[str] | Omit = omit, tools: Optional[Iterable[agent_update_params.Tool]] | Omit = omit, version: int | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsAgent: """Update Agent Args: description: Description. Omit to preserve; send empty string or null to clear. mcp_servers: MCP servers. Full replacement. Omit to preserve; send empty array or `null` to clear. Names must be unique. Maximum 20. Every server must be referenced by an `mcp_toolset` in the agent's resulting `tools`; unreferenced servers are rejected. See the [MCP connector guide](https://platform.claude.com/docs/en/managed-agents/mcp-connector). metadata: Metadata patch. Set a key to a string to upsert it, or to null to delete it. Omit the field to preserve. The stored bag is limited to 16 keys (up to 64 chars each) with values up to 512 chars. model: Model identifier. Accepts the [model string](https://platform.claude.com/docs/en/about-claude/models/overview#latest-models-comparison), e.g. `claude-opus-4-6`, or a `model_config` object for additional configuration control. Omit to preserve. Cannot be cleared. multiagent: A coordinator topology: the session's primary thread orchestrates work by spawning session threads, each running an agent drawn from the `agents` roster. name: Human-readable name. Must be non-empty. Omit to preserve. Cannot be cleared. skills: Skills. Full replacement. Omit to preserve; send empty array or null to clear. system: System prompt. Omit to preserve; send empty string or null to clear. tools: Tool configurations available to the agent. Full replacement. Omit to preserve; send empty array or null to clear. Maximum of 128 tools across all toolsets allowed. version: The agent's current version, used to prevent concurrent overwrites. Obtain this value from a create or retrieve response. Must be at least 1 if specified. When supplied, the request fails if it does not match the server's current version; omit to apply the update unconditionally. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._post( path_template("/v1/agents/{agent_id}?beta=true", agent_id=agent_id), body=maybe_transform( { "description": description, "mcp_servers": mcp_servers, "metadata": metadata, "model": model, "multiagent": multiagent, "name": name, "skills": skills, "system": system, "tools": tools, "version": version, }, agent_update_params.AgentUpdateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsAgent, ) def list( self, *, created_at_gte: Union[str, datetime] | Omit = omit, created_at_lte: Union[str, datetime] | Omit = omit, include_archived: bool | Omit = omit, limit: int | Omit = omit, page: str | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncPageCursor[BetaManagedAgentsAgent]: """ List Agents Args: created_at_gte: Return agents created at or after this time (inclusive). created_at_lte: Return agents created at or before this time (inclusive). include_archived: Include archived agents in results. Defaults to false. limit: Maximum results per page. Default 20, maximum 100. page: Opaque pagination cursor from a previous response. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get_api_list( "/v1/agents?beta=true", page=SyncPageCursor[BetaManagedAgentsAgent], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "created_at_gte": created_at_gte, "created_at_lte": created_at_lte, "include_archived": include_archived, "limit": limit, "page": page, }, agent_list_params.AgentListParams, ), ), model=BetaManagedAgentsAgent, ) def archive( self, agent_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsAgent: """ Archive Agent Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._post( path_template("/v1/agents/{agent_id}/archive?beta=true", agent_id=agent_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsAgent, ) class AsyncAgents(AsyncAPIResource): @cached_property def versions(self) -> AsyncVersions: return AsyncVersions(self._client) @cached_property def with_raw_response(self) -> AsyncAgentsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncAgentsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncAgentsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncAgentsWithStreamingResponse(self) async def create( self, *, model: agent_create_params.Model, name: str, description: Optional[str] | Omit = omit, mcp_servers: Iterable[BetaManagedAgentsURLMCPServerParams] | Omit = omit, metadata: Dict[str, str] | Omit = omit, multiagent: Optional[BetaManagedAgentsMultiagentParams] | Omit = omit, skills: Iterable[BetaManagedAgentsSkillParams] | Omit = omit, system: Optional[str] | Omit = omit, tools: Iterable[agent_create_params.Tool] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsAgent: """Create Agent Args: model: Model identifier. Accepts the [model string](https://platform.claude.com/docs/en/about-claude/models/overview#latest-models-comparison), e.g. `claude-opus-4-6`, or a `model_config` object for additional configuration control name: Human-readable name for the agent. description: Description of what the agent does. mcp_servers: MCP servers this agent connects to. Maximum 20. Names must be unique within the array. Every server must be referenced by an `mcp_toolset` in `tools`; unreferenced servers are rejected. See the [MCP connector guide](https://platform.claude.com/docs/en/managed-agents/mcp-connector). metadata: Arbitrary key-value metadata. Maximum 16 pairs, keys up to 64 chars, values up to 512 chars. multiagent: A coordinator topology: the session's primary thread orchestrates work by spawning session threads, each running an agent drawn from the `agents` roster. skills: Skills available to the agent. system: System prompt for the agent. tools: Tool configurations available to the agent. Maximum of 128 tools across all toolsets allowed. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._post( "/v1/agents?beta=true", body=await async_maybe_transform( { "model": model, "name": name, "description": description, "mcp_servers": mcp_servers, "metadata": metadata, "multiagent": multiagent, "skills": skills, "system": system, "tools": tools, }, agent_create_params.AgentCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsAgent, ) async def retrieve( self, agent_id: str, *, version: int | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsAgent: """Get Agent Args: version: Agent version. Omit for the most recent version. Must be at least 1 if specified. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._get( path_template("/v1/agents/{agent_id}?beta=true", agent_id=agent_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=await async_maybe_transform({"version": version}, agent_retrieve_params.AgentRetrieveParams), ), cast_to=BetaManagedAgentsAgent, ) async def update( self, agent_id: str, *, description: Optional[str] | Omit = omit, mcp_servers: Optional[Iterable[BetaManagedAgentsURLMCPServerParams]] | Omit = omit, metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, model: agent_update_params.Model | Omit = omit, multiagent: Optional[BetaManagedAgentsMultiagentParams] | Omit = omit, name: str | Omit = omit, skills: Optional[Iterable[BetaManagedAgentsSkillParams]] | Omit = omit, system: Optional[str] | Omit = omit, tools: Optional[Iterable[agent_update_params.Tool]] | Omit = omit, version: int | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsAgent: """Update Agent Args: description: Description. Omit to preserve; send empty string or null to clear. mcp_servers: MCP servers. Full replacement. Omit to preserve; send empty array or `null` to clear. Names must be unique. Maximum 20. Every server must be referenced by an `mcp_toolset` in the agent's resulting `tools`; unreferenced servers are rejected. See the [MCP connector guide](https://platform.claude.com/docs/en/managed-agents/mcp-connector). metadata: Metadata patch. Set a key to a string to upsert it, or to null to delete it. Omit the field to preserve. The stored bag is limited to 16 keys (up to 64 chars each) with values up to 512 chars. model: Model identifier. Accepts the [model string](https://platform.claude.com/docs/en/about-claude/models/overview#latest-models-comparison), e.g. `claude-opus-4-6`, or a `model_config` object for additional configuration control. Omit to preserve. Cannot be cleared. multiagent: A coordinator topology: the session's primary thread orchestrates work by spawning session threads, each running an agent drawn from the `agents` roster. name: Human-readable name. Must be non-empty. Omit to preserve. Cannot be cleared. skills: Skills. Full replacement. Omit to preserve; send empty array or null to clear. system: System prompt. Omit to preserve; send empty string or null to clear. tools: Tool configurations available to the agent. Full replacement. Omit to preserve; send empty array or null to clear. Maximum of 128 tools across all toolsets allowed. version: The agent's current version, used to prevent concurrent overwrites. Obtain this value from a create or retrieve response. Must be at least 1 if specified. When supplied, the request fails if it does not match the server's current version; omit to apply the update unconditionally. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._post( path_template("/v1/agents/{agent_id}?beta=true", agent_id=agent_id), body=await async_maybe_transform( { "description": description, "mcp_servers": mcp_servers, "metadata": metadata, "model": model, "multiagent": multiagent, "name": name, "skills": skills, "system": system, "tools": tools, "version": version, }, agent_update_params.AgentUpdateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsAgent, ) def list( self, *, created_at_gte: Union[str, datetime] | Omit = omit, created_at_lte: Union[str, datetime] | Omit = omit, include_archived: bool | Omit = omit, limit: int | Omit = omit, page: str | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[BetaManagedAgentsAgent, AsyncPageCursor[BetaManagedAgentsAgent]]: """ List Agents Args: created_at_gte: Return agents created at or after this time (inclusive). created_at_lte: Return agents created at or before this time (inclusive). include_archived: Include archived agents in results. Defaults to false. limit: Maximum results per page. Default 20, maximum 100. page: Opaque pagination cursor from a previous response. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get_api_list( "/v1/agents?beta=true", page=AsyncPageCursor[BetaManagedAgentsAgent], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "created_at_gte": created_at_gte, "created_at_lte": created_at_lte, "include_archived": include_archived, "limit": limit, "page": page, }, agent_list_params.AgentListParams, ), ), model=BetaManagedAgentsAgent, ) async def archive( self, agent_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsAgent: """ Archive Agent Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._post( path_template("/v1/agents/{agent_id}/archive?beta=true", agent_id=agent_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsAgent, ) class AgentsWithRawResponse: def __init__(self, agents: Agents) -> None: self._agents = agents self.create = _legacy_response.to_raw_response_wrapper( agents.create, ) self.retrieve = _legacy_response.to_raw_response_wrapper( agents.retrieve, ) self.update = _legacy_response.to_raw_response_wrapper( agents.update, ) self.list = _legacy_response.to_raw_response_wrapper( agents.list, ) self.archive = _legacy_response.to_raw_response_wrapper( agents.archive, ) @cached_property def versions(self) -> VersionsWithRawResponse: return VersionsWithRawResponse(self._agents.versions) class AsyncAgentsWithRawResponse: def __init__(self, agents: AsyncAgents) -> None: self._agents = agents self.create = _legacy_response.async_to_raw_response_wrapper( agents.create, ) self.retrieve = _legacy_response.async_to_raw_response_wrapper( agents.retrieve, ) self.update = _legacy_response.async_to_raw_response_wrapper( agents.update, ) self.list = _legacy_response.async_to_raw_response_wrapper( agents.list, ) self.archive = _legacy_response.async_to_raw_response_wrapper( agents.archive, ) @cached_property def versions(self) -> AsyncVersionsWithRawResponse: return AsyncVersionsWithRawResponse(self._agents.versions) class AgentsWithStreamingResponse: def __init__(self, agents: Agents) -> None: self._agents = agents self.create = to_streamed_response_wrapper( agents.create, ) self.retrieve = to_streamed_response_wrapper( agents.retrieve, ) self.update = to_streamed_response_wrapper( agents.update, ) self.list = to_streamed_response_wrapper( agents.list, ) self.archive = to_streamed_response_wrapper( agents.archive, ) @cached_property def versions(self) -> VersionsWithStreamingResponse: return VersionsWithStreamingResponse(self._agents.versions) class AsyncAgentsWithStreamingResponse: def __init__(self, agents: AsyncAgents) -> None: self._agents = agents self.create = async_to_streamed_response_wrapper( agents.create, ) self.retrieve = async_to_streamed_response_wrapper( agents.retrieve, ) self.update = async_to_streamed_response_wrapper( agents.update, ) self.list = async_to_streamed_response_wrapper( agents.list, ) self.archive = async_to_streamed_response_wrapper( agents.archive, ) @cached_property def versions(self) -> AsyncVersionsWithStreamingResponse: return AsyncVersionsWithStreamingResponse(self._agents.versions) anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/agents/versions.py000066400000000000000000000204351523216435200271560ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List from itertools import chain import httpx from .... import _legacy_response from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ...._utils import is_given, path_template, maybe_transform, strip_not_given from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ....pagination import SyncPageCursor, AsyncPageCursor from ...._base_client import AsyncPaginator, make_request_options from ....types.beta.agents import version_list_params from ....types.anthropic_beta_param import AnthropicBetaParam from ....types.beta.beta_managed_agents_agent import BetaManagedAgentsAgent __all__ = ["Versions", "AsyncVersions"] class Versions(SyncAPIResource): @cached_property def with_raw_response(self) -> VersionsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return VersionsWithRawResponse(self) @cached_property def with_streaming_response(self) -> VersionsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return VersionsWithStreamingResponse(self) def list( self, agent_id: str, *, limit: int | Omit = omit, page: str | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncPageCursor[BetaManagedAgentsAgent]: """List Agent Versions Args: limit: Maximum results per page. Default 20, maximum 100. page: Opaque pagination cursor. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get_api_list( path_template("/v1/agents/{agent_id}/versions?beta=true", agent_id=agent_id), page=SyncPageCursor[BetaManagedAgentsAgent], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "limit": limit, "page": page, }, version_list_params.VersionListParams, ), ), model=BetaManagedAgentsAgent, ) class AsyncVersions(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncVersionsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncVersionsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncVersionsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncVersionsWithStreamingResponse(self) def list( self, agent_id: str, *, limit: int | Omit = omit, page: str | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[BetaManagedAgentsAgent, AsyncPageCursor[BetaManagedAgentsAgent]]: """List Agent Versions Args: limit: Maximum results per page. Default 20, maximum 100. page: Opaque pagination cursor. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not agent_id: raise ValueError(f"Expected a non-empty value for `agent_id` but received {agent_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get_api_list( path_template("/v1/agents/{agent_id}/versions?beta=true", agent_id=agent_id), page=AsyncPageCursor[BetaManagedAgentsAgent], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "limit": limit, "page": page, }, version_list_params.VersionListParams, ), ), model=BetaManagedAgentsAgent, ) class VersionsWithRawResponse: def __init__(self, versions: Versions) -> None: self._versions = versions self.list = _legacy_response.to_raw_response_wrapper( versions.list, ) class AsyncVersionsWithRawResponse: def __init__(self, versions: AsyncVersions) -> None: self._versions = versions self.list = _legacy_response.async_to_raw_response_wrapper( versions.list, ) class VersionsWithStreamingResponse: def __init__(self, versions: Versions) -> None: self._versions = versions self.list = to_streamed_response_wrapper( versions.list, ) class AsyncVersionsWithStreamingResponse: def __init__(self, versions: AsyncVersions) -> None: self._versions = versions self.list = async_to_streamed_response_wrapper( versions.list, ) anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/beta.py000066400000000000000000000402361523216435200247410ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from .files import ( Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse, FilesWithStreamingResponse, AsyncFilesWithStreamingResponse, ) from .dreams import ( Dreams, AsyncDreams, DreamsWithRawResponse, AsyncDreamsWithRawResponse, DreamsWithStreamingResponse, AsyncDreamsWithStreamingResponse, ) from .models import ( Models, AsyncModels, ModelsWithRawResponse, AsyncModelsWithRawResponse, ModelsWithStreamingResponse, AsyncModelsWithStreamingResponse, ) from .webhooks import Webhooks, AsyncWebhooks from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from .deployments import ( Deployments, AsyncDeployments, DeploymentsWithRawResponse, AsyncDeploymentsWithRawResponse, DeploymentsWithStreamingResponse, AsyncDeploymentsWithStreamingResponse, ) from .agents.agents import ( Agents, AsyncAgents, AgentsWithRawResponse, AsyncAgentsWithRawResponse, AgentsWithStreamingResponse, AsyncAgentsWithStreamingResponse, ) from .skills.skills import ( Skills, AsyncSkills, SkillsWithRawResponse, AsyncSkillsWithRawResponse, SkillsWithStreamingResponse, AsyncSkillsWithStreamingResponse, ) from .user_profiles import ( UserProfiles, AsyncUserProfiles, UserProfilesWithRawResponse, AsyncUserProfilesWithRawResponse, UserProfilesWithStreamingResponse, AsyncUserProfilesWithStreamingResponse, ) from .vaults.vaults import ( Vaults, AsyncVaults, VaultsWithRawResponse, AsyncVaultsWithRawResponse, VaultsWithStreamingResponse, AsyncVaultsWithStreamingResponse, ) from .deployment_runs import ( DeploymentRuns, AsyncDeploymentRuns, DeploymentRunsWithRawResponse, AsyncDeploymentRunsWithRawResponse, DeploymentRunsWithStreamingResponse, AsyncDeploymentRunsWithStreamingResponse, ) from .tunnels.tunnels import ( Tunnels, AsyncTunnels, TunnelsWithRawResponse, AsyncTunnelsWithRawResponse, TunnelsWithStreamingResponse, AsyncTunnelsWithStreamingResponse, ) from .messages.messages import ( Messages, AsyncMessages, MessagesWithRawResponse, AsyncMessagesWithRawResponse, MessagesWithStreamingResponse, AsyncMessagesWithStreamingResponse, ) from .sessions.sessions import ( Sessions, AsyncSessions, SessionsWithRawResponse, AsyncSessionsWithRawResponse, SessionsWithStreamingResponse, AsyncSessionsWithStreamingResponse, ) from .environments.environments import ( Environments, AsyncEnvironments, EnvironmentsWithRawResponse, AsyncEnvironmentsWithRawResponse, EnvironmentsWithStreamingResponse, AsyncEnvironmentsWithStreamingResponse, ) from .memory_stores.memory_stores import ( MemoryStores, AsyncMemoryStores, MemoryStoresWithRawResponse, AsyncMemoryStoresWithRawResponse, MemoryStoresWithStreamingResponse, AsyncMemoryStoresWithStreamingResponse, ) __all__ = ["Beta", "AsyncBeta"] class Beta(SyncAPIResource): @cached_property def models(self) -> Models: return Models(self._client) @cached_property def messages(self) -> Messages: return Messages(self._client) @cached_property def agents(self) -> Agents: return Agents(self._client) @cached_property def environments(self) -> Environments: return Environments(self._client) @cached_property def sessions(self) -> Sessions: return Sessions(self._client) @cached_property def deployments(self) -> Deployments: return Deployments(self._client) @cached_property def deployment_runs(self) -> DeploymentRuns: return DeploymentRuns(self._client) @cached_property def vaults(self) -> Vaults: return Vaults(self._client) @cached_property def memory_stores(self) -> MemoryStores: return MemoryStores(self._client) @cached_property def files(self) -> Files: return Files(self._client) @cached_property def skills(self) -> Skills: return Skills(self._client) @cached_property def webhooks(self) -> Webhooks: return Webhooks(self._client) @cached_property def user_profiles(self) -> UserProfiles: return UserProfiles(self._client) @cached_property def dreams(self) -> Dreams: return Dreams(self._client) @cached_property def tunnels(self) -> Tunnels: return Tunnels(self._client) @cached_property def with_raw_response(self) -> BetaWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return BetaWithRawResponse(self) @cached_property def with_streaming_response(self) -> BetaWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return BetaWithStreamingResponse(self) class AsyncBeta(AsyncAPIResource): @cached_property def models(self) -> AsyncModels: return AsyncModels(self._client) @cached_property def messages(self) -> AsyncMessages: return AsyncMessages(self._client) @cached_property def agents(self) -> AsyncAgents: return AsyncAgents(self._client) @cached_property def environments(self) -> AsyncEnvironments: return AsyncEnvironments(self._client) @cached_property def sessions(self) -> AsyncSessions: return AsyncSessions(self._client) @cached_property def deployments(self) -> AsyncDeployments: return AsyncDeployments(self._client) @cached_property def deployment_runs(self) -> AsyncDeploymentRuns: return AsyncDeploymentRuns(self._client) @cached_property def vaults(self) -> AsyncVaults: return AsyncVaults(self._client) @cached_property def memory_stores(self) -> AsyncMemoryStores: return AsyncMemoryStores(self._client) @cached_property def files(self) -> AsyncFiles: return AsyncFiles(self._client) @cached_property def skills(self) -> AsyncSkills: return AsyncSkills(self._client) @cached_property def webhooks(self) -> AsyncWebhooks: return AsyncWebhooks(self._client) @cached_property def user_profiles(self) -> AsyncUserProfiles: return AsyncUserProfiles(self._client) @cached_property def dreams(self) -> AsyncDreams: return AsyncDreams(self._client) @cached_property def tunnels(self) -> AsyncTunnels: return AsyncTunnels(self._client) @cached_property def with_raw_response(self) -> AsyncBetaWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncBetaWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncBetaWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncBetaWithStreamingResponse(self) class BetaWithRawResponse: def __init__(self, beta: Beta) -> None: self._beta = beta @cached_property def models(self) -> ModelsWithRawResponse: return ModelsWithRawResponse(self._beta.models) @cached_property def messages(self) -> MessagesWithRawResponse: return MessagesWithRawResponse(self._beta.messages) @cached_property def agents(self) -> AgentsWithRawResponse: return AgentsWithRawResponse(self._beta.agents) @cached_property def environments(self) -> EnvironmentsWithRawResponse: return EnvironmentsWithRawResponse(self._beta.environments) @cached_property def sessions(self) -> SessionsWithRawResponse: return SessionsWithRawResponse(self._beta.sessions) @cached_property def deployments(self) -> DeploymentsWithRawResponse: return DeploymentsWithRawResponse(self._beta.deployments) @cached_property def deployment_runs(self) -> DeploymentRunsWithRawResponse: return DeploymentRunsWithRawResponse(self._beta.deployment_runs) @cached_property def vaults(self) -> VaultsWithRawResponse: return VaultsWithRawResponse(self._beta.vaults) @cached_property def memory_stores(self) -> MemoryStoresWithRawResponse: return MemoryStoresWithRawResponse(self._beta.memory_stores) @cached_property def files(self) -> FilesWithRawResponse: return FilesWithRawResponse(self._beta.files) @cached_property def skills(self) -> SkillsWithRawResponse: return SkillsWithRawResponse(self._beta.skills) @cached_property def user_profiles(self) -> UserProfilesWithRawResponse: return UserProfilesWithRawResponse(self._beta.user_profiles) @cached_property def dreams(self) -> DreamsWithRawResponse: return DreamsWithRawResponse(self._beta.dreams) @cached_property def tunnels(self) -> TunnelsWithRawResponse: return TunnelsWithRawResponse(self._beta.tunnels) class AsyncBetaWithRawResponse: def __init__(self, beta: AsyncBeta) -> None: self._beta = beta @cached_property def models(self) -> AsyncModelsWithRawResponse: return AsyncModelsWithRawResponse(self._beta.models) @cached_property def messages(self) -> AsyncMessagesWithRawResponse: return AsyncMessagesWithRawResponse(self._beta.messages) @cached_property def agents(self) -> AsyncAgentsWithRawResponse: return AsyncAgentsWithRawResponse(self._beta.agents) @cached_property def environments(self) -> AsyncEnvironmentsWithRawResponse: return AsyncEnvironmentsWithRawResponse(self._beta.environments) @cached_property def sessions(self) -> AsyncSessionsWithRawResponse: return AsyncSessionsWithRawResponse(self._beta.sessions) @cached_property def deployments(self) -> AsyncDeploymentsWithRawResponse: return AsyncDeploymentsWithRawResponse(self._beta.deployments) @cached_property def deployment_runs(self) -> AsyncDeploymentRunsWithRawResponse: return AsyncDeploymentRunsWithRawResponse(self._beta.deployment_runs) @cached_property def vaults(self) -> AsyncVaultsWithRawResponse: return AsyncVaultsWithRawResponse(self._beta.vaults) @cached_property def memory_stores(self) -> AsyncMemoryStoresWithRawResponse: return AsyncMemoryStoresWithRawResponse(self._beta.memory_stores) @cached_property def files(self) -> AsyncFilesWithRawResponse: return AsyncFilesWithRawResponse(self._beta.files) @cached_property def skills(self) -> AsyncSkillsWithRawResponse: return AsyncSkillsWithRawResponse(self._beta.skills) @cached_property def user_profiles(self) -> AsyncUserProfilesWithRawResponse: return AsyncUserProfilesWithRawResponse(self._beta.user_profiles) @cached_property def dreams(self) -> AsyncDreamsWithRawResponse: return AsyncDreamsWithRawResponse(self._beta.dreams) @cached_property def tunnels(self) -> AsyncTunnelsWithRawResponse: return AsyncTunnelsWithRawResponse(self._beta.tunnels) class BetaWithStreamingResponse: def __init__(self, beta: Beta) -> None: self._beta = beta @cached_property def models(self) -> ModelsWithStreamingResponse: return ModelsWithStreamingResponse(self._beta.models) @cached_property def messages(self) -> MessagesWithStreamingResponse: return MessagesWithStreamingResponse(self._beta.messages) @cached_property def agents(self) -> AgentsWithStreamingResponse: return AgentsWithStreamingResponse(self._beta.agents) @cached_property def environments(self) -> EnvironmentsWithStreamingResponse: return EnvironmentsWithStreamingResponse(self._beta.environments) @cached_property def sessions(self) -> SessionsWithStreamingResponse: return SessionsWithStreamingResponse(self._beta.sessions) @cached_property def deployments(self) -> DeploymentsWithStreamingResponse: return DeploymentsWithStreamingResponse(self._beta.deployments) @cached_property def deployment_runs(self) -> DeploymentRunsWithStreamingResponse: return DeploymentRunsWithStreamingResponse(self._beta.deployment_runs) @cached_property def vaults(self) -> VaultsWithStreamingResponse: return VaultsWithStreamingResponse(self._beta.vaults) @cached_property def memory_stores(self) -> MemoryStoresWithStreamingResponse: return MemoryStoresWithStreamingResponse(self._beta.memory_stores) @cached_property def files(self) -> FilesWithStreamingResponse: return FilesWithStreamingResponse(self._beta.files) @cached_property def skills(self) -> SkillsWithStreamingResponse: return SkillsWithStreamingResponse(self._beta.skills) @cached_property def user_profiles(self) -> UserProfilesWithStreamingResponse: return UserProfilesWithStreamingResponse(self._beta.user_profiles) @cached_property def dreams(self) -> DreamsWithStreamingResponse: return DreamsWithStreamingResponse(self._beta.dreams) @cached_property def tunnels(self) -> TunnelsWithStreamingResponse: return TunnelsWithStreamingResponse(self._beta.tunnels) class AsyncBetaWithStreamingResponse: def __init__(self, beta: AsyncBeta) -> None: self._beta = beta @cached_property def models(self) -> AsyncModelsWithStreamingResponse: return AsyncModelsWithStreamingResponse(self._beta.models) @cached_property def messages(self) -> AsyncMessagesWithStreamingResponse: return AsyncMessagesWithStreamingResponse(self._beta.messages) @cached_property def agents(self) -> AsyncAgentsWithStreamingResponse: return AsyncAgentsWithStreamingResponse(self._beta.agents) @cached_property def environments(self) -> AsyncEnvironmentsWithStreamingResponse: return AsyncEnvironmentsWithStreamingResponse(self._beta.environments) @cached_property def sessions(self) -> AsyncSessionsWithStreamingResponse: return AsyncSessionsWithStreamingResponse(self._beta.sessions) @cached_property def deployments(self) -> AsyncDeploymentsWithStreamingResponse: return AsyncDeploymentsWithStreamingResponse(self._beta.deployments) @cached_property def deployment_runs(self) -> AsyncDeploymentRunsWithStreamingResponse: return AsyncDeploymentRunsWithStreamingResponse(self._beta.deployment_runs) @cached_property def vaults(self) -> AsyncVaultsWithStreamingResponse: return AsyncVaultsWithStreamingResponse(self._beta.vaults) @cached_property def memory_stores(self) -> AsyncMemoryStoresWithStreamingResponse: return AsyncMemoryStoresWithStreamingResponse(self._beta.memory_stores) @cached_property def files(self) -> AsyncFilesWithStreamingResponse: return AsyncFilesWithStreamingResponse(self._beta.files) @cached_property def skills(self) -> AsyncSkillsWithStreamingResponse: return AsyncSkillsWithStreamingResponse(self._beta.skills) @cached_property def user_profiles(self) -> AsyncUserProfilesWithStreamingResponse: return AsyncUserProfilesWithStreamingResponse(self._beta.user_profiles) @cached_property def dreams(self) -> AsyncDreamsWithStreamingResponse: return AsyncDreamsWithStreamingResponse(self._beta.dreams) @cached_property def tunnels(self) -> AsyncTunnelsWithStreamingResponse: return AsyncTunnelsWithStreamingResponse(self._beta.tunnels) anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/deployment_runs.py000066400000000000000000000376221523216435200272620ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Union from datetime import datetime from itertools import chain import httpx from ... import _legacy_response from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ..._utils import is_given, path_template, maybe_transform, strip_not_given from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ...pagination import SyncPageCursor, AsyncPageCursor from ...types.beta import BetaManagedAgentsTriggerType, deployment_run_list_params from ..._base_client import AsyncPaginator, make_request_options from ...types.anthropic_beta_param import AnthropicBetaParam from ...types.beta.beta_managed_agents_trigger_type import BetaManagedAgentsTriggerType from ...types.beta.beta_managed_agents_deployment_run import BetaManagedAgentsDeploymentRun __all__ = ["DeploymentRuns", "AsyncDeploymentRuns"] class DeploymentRuns(SyncAPIResource): @cached_property def with_raw_response(self) -> DeploymentRunsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return DeploymentRunsWithRawResponse(self) @cached_property def with_streaming_response(self) -> DeploymentRunsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return DeploymentRunsWithStreamingResponse(self) def retrieve( self, deployment_run_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsDeploymentRun: """ Get Deployment Run Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not deployment_run_id: raise ValueError(f"Expected a non-empty value for `deployment_run_id` but received {deployment_run_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get( path_template("/v1/deployment_runs/{deployment_run_id}?beta=true", deployment_run_id=deployment_run_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsDeploymentRun, ) def list( self, *, created_at_gt: Union[str, datetime] | Omit = omit, created_at_gte: Union[str, datetime] | Omit = omit, created_at_lt: Union[str, datetime] | Omit = omit, created_at_lte: Union[str, datetime] | Omit = omit, deployment_id: str | Omit = omit, has_error: bool | Omit = omit, limit: int | Omit = omit, page: str | Omit = omit, trigger_type: BetaManagedAgentsTriggerType | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncPageCursor[BetaManagedAgentsDeploymentRun]: """ List Deployment Runs Args: created_at_gt: Return runs created strictly after this time (exclusive). created_at_gte: Return runs created at or after this time (inclusive). created_at_lt: Return runs created strictly before this time (exclusive). created_at_lte: Return runs created at or before this time (inclusive). deployment_id: Filter to a specific deployment. Omit to list across all deployments in the workspace. Filtering by a non-existent deployment_id returns 200 with empty data. has_error: Filter: true for runs with non-null error, false for runs with non-null session_id. Omit for all. limit: Maximum results per page. Default 20, maximum 1000. page: Opaque pagination cursor. Pass next_page from the previous response. Invalid or expired cursors return 400. trigger_type: Filter runs by what triggered them. Omit to return all runs. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get_api_list( "/v1/deployment_runs?beta=true", page=SyncPageCursor[BetaManagedAgentsDeploymentRun], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "created_at_gt": created_at_gt, "created_at_gte": created_at_gte, "created_at_lt": created_at_lt, "created_at_lte": created_at_lte, "deployment_id": deployment_id, "has_error": has_error, "limit": limit, "page": page, "trigger_type": trigger_type, }, deployment_run_list_params.DeploymentRunListParams, ), ), model=BetaManagedAgentsDeploymentRun, ) class AsyncDeploymentRuns(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncDeploymentRunsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncDeploymentRunsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncDeploymentRunsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncDeploymentRunsWithStreamingResponse(self) async def retrieve( self, deployment_run_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsDeploymentRun: """ Get Deployment Run Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not deployment_run_id: raise ValueError(f"Expected a non-empty value for `deployment_run_id` but received {deployment_run_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._get( path_template("/v1/deployment_runs/{deployment_run_id}?beta=true", deployment_run_id=deployment_run_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsDeploymentRun, ) def list( self, *, created_at_gt: Union[str, datetime] | Omit = omit, created_at_gte: Union[str, datetime] | Omit = omit, created_at_lt: Union[str, datetime] | Omit = omit, created_at_lte: Union[str, datetime] | Omit = omit, deployment_id: str | Omit = omit, has_error: bool | Omit = omit, limit: int | Omit = omit, page: str | Omit = omit, trigger_type: BetaManagedAgentsTriggerType | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[BetaManagedAgentsDeploymentRun, AsyncPageCursor[BetaManagedAgentsDeploymentRun]]: """ List Deployment Runs Args: created_at_gt: Return runs created strictly after this time (exclusive). created_at_gte: Return runs created at or after this time (inclusive). created_at_lt: Return runs created strictly before this time (exclusive). created_at_lte: Return runs created at or before this time (inclusive). deployment_id: Filter to a specific deployment. Omit to list across all deployments in the workspace. Filtering by a non-existent deployment_id returns 200 with empty data. has_error: Filter: true for runs with non-null error, false for runs with non-null session_id. Omit for all. limit: Maximum results per page. Default 20, maximum 1000. page: Opaque pagination cursor. Pass next_page from the previous response. Invalid or expired cursors return 400. trigger_type: Filter runs by what triggered them. Omit to return all runs. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get_api_list( "/v1/deployment_runs?beta=true", page=AsyncPageCursor[BetaManagedAgentsDeploymentRun], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "created_at_gt": created_at_gt, "created_at_gte": created_at_gte, "created_at_lt": created_at_lt, "created_at_lte": created_at_lte, "deployment_id": deployment_id, "has_error": has_error, "limit": limit, "page": page, "trigger_type": trigger_type, }, deployment_run_list_params.DeploymentRunListParams, ), ), model=BetaManagedAgentsDeploymentRun, ) class DeploymentRunsWithRawResponse: def __init__(self, deployment_runs: DeploymentRuns) -> None: self._deployment_runs = deployment_runs self.retrieve = _legacy_response.to_raw_response_wrapper( deployment_runs.retrieve, ) self.list = _legacy_response.to_raw_response_wrapper( deployment_runs.list, ) class AsyncDeploymentRunsWithRawResponse: def __init__(self, deployment_runs: AsyncDeploymentRuns) -> None: self._deployment_runs = deployment_runs self.retrieve = _legacy_response.async_to_raw_response_wrapper( deployment_runs.retrieve, ) self.list = _legacy_response.async_to_raw_response_wrapper( deployment_runs.list, ) class DeploymentRunsWithStreamingResponse: def __init__(self, deployment_runs: DeploymentRuns) -> None: self._deployment_runs = deployment_runs self.retrieve = to_streamed_response_wrapper( deployment_runs.retrieve, ) self.list = to_streamed_response_wrapper( deployment_runs.list, ) class AsyncDeploymentRunsWithStreamingResponse: def __init__(self, deployment_runs: AsyncDeploymentRuns) -> None: self._deployment_runs = deployment_runs self.retrieve = async_to_streamed_response_wrapper( deployment_runs.retrieve, ) self.list = async_to_streamed_response_wrapper( deployment_runs.list, ) anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/deployments.py000066400000000000000000001415571523216435200264010ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Union, Iterable, Optional from datetime import datetime from itertools import chain import httpx from ... import _legacy_response from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given from ..._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ...pagination import SyncPageCursor, AsyncPageCursor from ...types.beta import ( BetaManagedAgentsScheduleParams, BetaManagedAgentsDeploymentStatus, deployment_list_params, deployment_create_params, deployment_update_params, ) from ..._base_client import AsyncPaginator, make_request_options from ...types.anthropic_beta_param import AnthropicBetaParam from ...types.beta.beta_managed_agents_deployment import BetaManagedAgentsDeployment from ...types.beta.beta_managed_agents_deployment_run import BetaManagedAgentsDeploymentRun from ...types.beta.beta_managed_agents_schedule_params import BetaManagedAgentsScheduleParams from ...types.beta.beta_managed_agents_deployment_status import BetaManagedAgentsDeploymentStatus from ...types.beta.beta_managed_agents_deployment_initial_event_params import ( BetaManagedAgentsDeploymentInitialEventParams, ) __all__ = ["Deployments", "AsyncDeployments"] class Deployments(SyncAPIResource): @cached_property def with_raw_response(self) -> DeploymentsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return DeploymentsWithRawResponse(self) @cached_property def with_streaming_response(self) -> DeploymentsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return DeploymentsWithStreamingResponse(self) def create( self, *, agent: deployment_create_params.Agent, environment_id: str, initial_events: Iterable[BetaManagedAgentsDeploymentInitialEventParams], name: str, description: Optional[str] | Omit = omit, metadata: Dict[str, str] | Omit = omit, resources: Iterable[deployment_create_params.Resource] | Omit = omit, schedule: Optional[BetaManagedAgentsScheduleParams] | Omit = omit, vault_ids: SequenceNotStr[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsDeployment: """Create Deployment Args: agent: Agent to deploy. Accepts the `agent` ID string, which pins the latest version, or an `agent` object with both id and version specified. The agent must exist and not be archived. environment_id: ID of the `environment` defining the container configuration for sessions created from this deployment. initial_events: Events to send to each session immediately after creation. At least 1, maximum 50. name: Human-readable name for the deployment. description: Description of what the deployment does. metadata: Arbitrary key-value metadata. Maximum 16 pairs, keys up to 64 chars, values up to 512 chars. resources: Resources (e.g. repositories, files) to mount into each session's container. Maximum 500. schedule: 5-field POSIX cron schedule. Literal wall-clock matching in the configured timezone. vault_ids: Vault IDs for stored credentials the agent can use during sessions created from this deployment. Maximum 50. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._post( "/v1/deployments?beta=true", body=maybe_transform( { "agent": agent, "environment_id": environment_id, "initial_events": initial_events, "name": name, "description": description, "metadata": metadata, "resources": resources, "schedule": schedule, "vault_ids": vault_ids, }, deployment_create_params.DeploymentCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsDeployment, ) def retrieve( self, deployment_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsDeployment: """ Get Deployment Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not deployment_id: raise ValueError(f"Expected a non-empty value for `deployment_id` but received {deployment_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get( path_template("/v1/deployments/{deployment_id}?beta=true", deployment_id=deployment_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsDeployment, ) def update( self, deployment_id: str, *, agent: deployment_update_params.Agent | Omit = omit, description: Optional[str] | Omit = omit, environment_id: str | Omit = omit, initial_events: Iterable[BetaManagedAgentsDeploymentInitialEventParams] | Omit = omit, metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, name: str | Omit = omit, resources: Optional[Iterable[deployment_update_params.Resource]] | Omit = omit, schedule: Optional[BetaManagedAgentsScheduleParams] | Omit = omit, vault_ids: Optional[SequenceNotStr[str]] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsDeployment: """Update Deployment Args: agent: Agent to deploy. Accepts the `agent` ID string, which re-pins to the latest version, or an `agent` object with both id and version specified. Omit to preserve. Cannot be cleared. description: Description. Omit to preserve; send empty string or null to clear. environment_id: ID of the `environment` where sessions run. Omit to preserve. Cannot be cleared. initial_events: Initial events. Full replacement. Omit to preserve. Cannot be cleared. At least 1, maximum 50. metadata: Metadata patch. Set a key to a string to upsert it, or to null to delete it. Omit the field to preserve. The stored bag is limited to 16 keys (up to 64 chars each) with values up to 512 chars. name: Human-readable name. Must be non-empty. Omit to preserve. Cannot be cleared. resources: Session resources. Full replacement. Omit to preserve; send empty array or null to clear. Maximum 500. schedule: 5-field POSIX cron schedule. Literal wall-clock matching in the configured timezone. vault_ids: Vault IDs. Full replacement. Omit to preserve; send empty array or null to clear. Maximum 50. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not deployment_id: raise ValueError(f"Expected a non-empty value for `deployment_id` but received {deployment_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._post( path_template("/v1/deployments/{deployment_id}?beta=true", deployment_id=deployment_id), body=maybe_transform( { "agent": agent, "description": description, "environment_id": environment_id, "initial_events": initial_events, "metadata": metadata, "name": name, "resources": resources, "schedule": schedule, "vault_ids": vault_ids, }, deployment_update_params.DeploymentUpdateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsDeployment, ) def list( self, *, agent_id: str | Omit = omit, created_at_gte: Union[str, datetime] | Omit = omit, created_at_lte: Union[str, datetime] | Omit = omit, include_archived: bool | Omit = omit, limit: int | Omit = omit, page: str | Omit = omit, status: BetaManagedAgentsDeploymentStatus | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncPageCursor[BetaManagedAgentsDeployment]: """ List Deployments Args: agent_id: Filter by agent ID. created_at_gte: Return deployments created at or after this time (inclusive). created_at_lte: Return deployments created at or before this time (inclusive). include_archived: When true, includes archived deployments. Default: false (exclude archived). limit: Maximum results per page. Default 20, maximum 100. page: Opaque pagination cursor. status: Filter by status: active or paused. Omit for both. To include archived deployments, use include_archived instead; the two cannot be combined. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get_api_list( "/v1/deployments?beta=true", page=SyncPageCursor[BetaManagedAgentsDeployment], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "agent_id": agent_id, "created_at_gte": created_at_gte, "created_at_lte": created_at_lte, "include_archived": include_archived, "limit": limit, "page": page, "status": status, }, deployment_list_params.DeploymentListParams, ), ), model=BetaManagedAgentsDeployment, ) def archive( self, deployment_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsDeployment: """ Archive Deployment Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not deployment_id: raise ValueError(f"Expected a non-empty value for `deployment_id` but received {deployment_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._post( path_template("/v1/deployments/{deployment_id}/archive?beta=true", deployment_id=deployment_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsDeployment, ) def pause( self, deployment_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsDeployment: """ Pause Deployment Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not deployment_id: raise ValueError(f"Expected a non-empty value for `deployment_id` but received {deployment_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._post( path_template("/v1/deployments/{deployment_id}/pause?beta=true", deployment_id=deployment_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsDeployment, ) def run( self, deployment_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsDeploymentRun: """ Run Deployment Now Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not deployment_id: raise ValueError(f"Expected a non-empty value for `deployment_id` but received {deployment_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._post( path_template("/v1/deployments/{deployment_id}/run?beta=true", deployment_id=deployment_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsDeploymentRun, ) def unpause( self, deployment_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsDeployment: """ Unpause Deployment Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not deployment_id: raise ValueError(f"Expected a non-empty value for `deployment_id` but received {deployment_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._post( path_template("/v1/deployments/{deployment_id}/unpause?beta=true", deployment_id=deployment_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsDeployment, ) class AsyncDeployments(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncDeploymentsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncDeploymentsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncDeploymentsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncDeploymentsWithStreamingResponse(self) async def create( self, *, agent: deployment_create_params.Agent, environment_id: str, initial_events: Iterable[BetaManagedAgentsDeploymentInitialEventParams], name: str, description: Optional[str] | Omit = omit, metadata: Dict[str, str] | Omit = omit, resources: Iterable[deployment_create_params.Resource] | Omit = omit, schedule: Optional[BetaManagedAgentsScheduleParams] | Omit = omit, vault_ids: SequenceNotStr[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsDeployment: """Create Deployment Args: agent: Agent to deploy. Accepts the `agent` ID string, which pins the latest version, or an `agent` object with both id and version specified. The agent must exist and not be archived. environment_id: ID of the `environment` defining the container configuration for sessions created from this deployment. initial_events: Events to send to each session immediately after creation. At least 1, maximum 50. name: Human-readable name for the deployment. description: Description of what the deployment does. metadata: Arbitrary key-value metadata. Maximum 16 pairs, keys up to 64 chars, values up to 512 chars. resources: Resources (e.g. repositories, files) to mount into each session's container. Maximum 500. schedule: 5-field POSIX cron schedule. Literal wall-clock matching in the configured timezone. vault_ids: Vault IDs for stored credentials the agent can use during sessions created from this deployment. Maximum 50. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._post( "/v1/deployments?beta=true", body=await async_maybe_transform( { "agent": agent, "environment_id": environment_id, "initial_events": initial_events, "name": name, "description": description, "metadata": metadata, "resources": resources, "schedule": schedule, "vault_ids": vault_ids, }, deployment_create_params.DeploymentCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsDeployment, ) async def retrieve( self, deployment_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsDeployment: """ Get Deployment Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not deployment_id: raise ValueError(f"Expected a non-empty value for `deployment_id` but received {deployment_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._get( path_template("/v1/deployments/{deployment_id}?beta=true", deployment_id=deployment_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsDeployment, ) async def update( self, deployment_id: str, *, agent: deployment_update_params.Agent | Omit = omit, description: Optional[str] | Omit = omit, environment_id: str | Omit = omit, initial_events: Iterable[BetaManagedAgentsDeploymentInitialEventParams] | Omit = omit, metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, name: str | Omit = omit, resources: Optional[Iterable[deployment_update_params.Resource]] | Omit = omit, schedule: Optional[BetaManagedAgentsScheduleParams] | Omit = omit, vault_ids: Optional[SequenceNotStr[str]] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsDeployment: """Update Deployment Args: agent: Agent to deploy. Accepts the `agent` ID string, which re-pins to the latest version, or an `agent` object with both id and version specified. Omit to preserve. Cannot be cleared. description: Description. Omit to preserve; send empty string or null to clear. environment_id: ID of the `environment` where sessions run. Omit to preserve. Cannot be cleared. initial_events: Initial events. Full replacement. Omit to preserve. Cannot be cleared. At least 1, maximum 50. metadata: Metadata patch. Set a key to a string to upsert it, or to null to delete it. Omit the field to preserve. The stored bag is limited to 16 keys (up to 64 chars each) with values up to 512 chars. name: Human-readable name. Must be non-empty. Omit to preserve. Cannot be cleared. resources: Session resources. Full replacement. Omit to preserve; send empty array or null to clear. Maximum 500. schedule: 5-field POSIX cron schedule. Literal wall-clock matching in the configured timezone. vault_ids: Vault IDs. Full replacement. Omit to preserve; send empty array or null to clear. Maximum 50. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not deployment_id: raise ValueError(f"Expected a non-empty value for `deployment_id` but received {deployment_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._post( path_template("/v1/deployments/{deployment_id}?beta=true", deployment_id=deployment_id), body=await async_maybe_transform( { "agent": agent, "description": description, "environment_id": environment_id, "initial_events": initial_events, "metadata": metadata, "name": name, "resources": resources, "schedule": schedule, "vault_ids": vault_ids, }, deployment_update_params.DeploymentUpdateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsDeployment, ) def list( self, *, agent_id: str | Omit = omit, created_at_gte: Union[str, datetime] | Omit = omit, created_at_lte: Union[str, datetime] | Omit = omit, include_archived: bool | Omit = omit, limit: int | Omit = omit, page: str | Omit = omit, status: BetaManagedAgentsDeploymentStatus | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[BetaManagedAgentsDeployment, AsyncPageCursor[BetaManagedAgentsDeployment]]: """ List Deployments Args: agent_id: Filter by agent ID. created_at_gte: Return deployments created at or after this time (inclusive). created_at_lte: Return deployments created at or before this time (inclusive). include_archived: When true, includes archived deployments. Default: false (exclude archived). limit: Maximum results per page. Default 20, maximum 100. page: Opaque pagination cursor. status: Filter by status: active or paused. Omit for both. To include archived deployments, use include_archived instead; the two cannot be combined. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get_api_list( "/v1/deployments?beta=true", page=AsyncPageCursor[BetaManagedAgentsDeployment], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "agent_id": agent_id, "created_at_gte": created_at_gte, "created_at_lte": created_at_lte, "include_archived": include_archived, "limit": limit, "page": page, "status": status, }, deployment_list_params.DeploymentListParams, ), ), model=BetaManagedAgentsDeployment, ) async def archive( self, deployment_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsDeployment: """ Archive Deployment Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not deployment_id: raise ValueError(f"Expected a non-empty value for `deployment_id` but received {deployment_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._post( path_template("/v1/deployments/{deployment_id}/archive?beta=true", deployment_id=deployment_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsDeployment, ) async def pause( self, deployment_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsDeployment: """ Pause Deployment Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not deployment_id: raise ValueError(f"Expected a non-empty value for `deployment_id` but received {deployment_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._post( path_template("/v1/deployments/{deployment_id}/pause?beta=true", deployment_id=deployment_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsDeployment, ) async def run( self, deployment_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsDeploymentRun: """ Run Deployment Now Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not deployment_id: raise ValueError(f"Expected a non-empty value for `deployment_id` but received {deployment_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._post( path_template("/v1/deployments/{deployment_id}/run?beta=true", deployment_id=deployment_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsDeploymentRun, ) async def unpause( self, deployment_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsDeployment: """ Unpause Deployment Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not deployment_id: raise ValueError(f"Expected a non-empty value for `deployment_id` but received {deployment_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._post( path_template("/v1/deployments/{deployment_id}/unpause?beta=true", deployment_id=deployment_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsDeployment, ) class DeploymentsWithRawResponse: def __init__(self, deployments: Deployments) -> None: self._deployments = deployments self.create = _legacy_response.to_raw_response_wrapper( deployments.create, ) self.retrieve = _legacy_response.to_raw_response_wrapper( deployments.retrieve, ) self.update = _legacy_response.to_raw_response_wrapper( deployments.update, ) self.list = _legacy_response.to_raw_response_wrapper( deployments.list, ) self.archive = _legacy_response.to_raw_response_wrapper( deployments.archive, ) self.pause = _legacy_response.to_raw_response_wrapper( deployments.pause, ) self.run = _legacy_response.to_raw_response_wrapper( deployments.run, ) self.unpause = _legacy_response.to_raw_response_wrapper( deployments.unpause, ) class AsyncDeploymentsWithRawResponse: def __init__(self, deployments: AsyncDeployments) -> None: self._deployments = deployments self.create = _legacy_response.async_to_raw_response_wrapper( deployments.create, ) self.retrieve = _legacy_response.async_to_raw_response_wrapper( deployments.retrieve, ) self.update = _legacy_response.async_to_raw_response_wrapper( deployments.update, ) self.list = _legacy_response.async_to_raw_response_wrapper( deployments.list, ) self.archive = _legacy_response.async_to_raw_response_wrapper( deployments.archive, ) self.pause = _legacy_response.async_to_raw_response_wrapper( deployments.pause, ) self.run = _legacy_response.async_to_raw_response_wrapper( deployments.run, ) self.unpause = _legacy_response.async_to_raw_response_wrapper( deployments.unpause, ) class DeploymentsWithStreamingResponse: def __init__(self, deployments: Deployments) -> None: self._deployments = deployments self.create = to_streamed_response_wrapper( deployments.create, ) self.retrieve = to_streamed_response_wrapper( deployments.retrieve, ) self.update = to_streamed_response_wrapper( deployments.update, ) self.list = to_streamed_response_wrapper( deployments.list, ) self.archive = to_streamed_response_wrapper( deployments.archive, ) self.pause = to_streamed_response_wrapper( deployments.pause, ) self.run = to_streamed_response_wrapper( deployments.run, ) self.unpause = to_streamed_response_wrapper( deployments.unpause, ) class AsyncDeploymentsWithStreamingResponse: def __init__(self, deployments: AsyncDeployments) -> None: self._deployments = deployments self.create = async_to_streamed_response_wrapper( deployments.create, ) self.retrieve = async_to_streamed_response_wrapper( deployments.retrieve, ) self.update = async_to_streamed_response_wrapper( deployments.update, ) self.list = async_to_streamed_response_wrapper( deployments.list, ) self.archive = async_to_streamed_response_wrapper( deployments.archive, ) self.pause = async_to_streamed_response_wrapper( deployments.pause, ) self.run = async_to_streamed_response_wrapper( deployments.run, ) self.unpause = async_to_streamed_response_wrapper( deployments.unpause, ) anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/dreams.py000066400000000000000000000650001523216435200252750ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Union, Iterable, Optional from datetime import datetime from itertools import chain import httpx from ... import _legacy_response from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ..._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ...pagination import SyncPageCursor, AsyncPageCursor from ...types.beta import dream_list_params, dream_create_params from ..._base_client import AsyncPaginator, make_request_options from ...types.beta.beta_dream import BetaDream from ...types.anthropic_beta_param import AnthropicBetaParam from ...types.beta.beta_dream_status import BetaDreamStatus from ...types.beta.beta_dream_input_param import BetaDreamInputParam __all__ = ["Dreams", "AsyncDreams"] class Dreams(SyncAPIResource): @cached_property def with_raw_response(self) -> DreamsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return DreamsWithRawResponse(self) @cached_property def with_streaming_response(self) -> DreamsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return DreamsWithStreamingResponse(self) def create( self, *, inputs: Iterable[BetaDreamInputParam], model: dream_create_params.Model, instructions: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaDream: """ Create a Dream Args: model: Model identifier and configuration applied to every pipeline stage. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["dreaming-2026-04-21"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "dreaming-2026-04-21", **(extra_headers or {})} return self._post( "/v1/dreams?beta=true", body=maybe_transform( { "inputs": inputs, "model": model, "instructions": instructions, }, dream_create_params.DreamCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaDream, ) def retrieve( self, dream_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaDream: """ Get a Dream Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not dream_id: raise ValueError(f"Expected a non-empty value for `dream_id` but received {dream_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["dreaming-2026-04-21"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "dreaming-2026-04-21", **(extra_headers or {})} return self._get( path_template("/v1/dreams/{dream_id}?beta=true", dream_id=dream_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaDream, ) def list( self, *, created_at_gt: Union[str, datetime] | Omit = omit, created_at_lt: Union[str, datetime] | Omit = omit, include_archived: bool | Omit = omit, limit: int | Omit = omit, page: str | Omit = omit, statuses: List[BetaDreamStatus] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncPageCursor[BetaDream]: """ List Dreams Args: created_at_gt: Return dreams with `created_at` strictly after this timestamp (exclusive lower bound, RFC 3339). Unset applies no lower bound. created_at_lt: Return dreams with `created_at` strictly before this timestamp (exclusive upper bound, RFC 3339). Unset applies no upper bound. include_archived: Query parameter for include_archived limit: Query parameter for limit page: Query parameter for page statuses: Filter by lifecycle status. Repeat the parameter to match any of multiple statuses. Empty applies no status filter. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["dreaming-2026-04-21"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "dreaming-2026-04-21", **(extra_headers or {})} return self._get_api_list( "/v1/dreams?beta=true", page=SyncPageCursor[BetaDream], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "created_at_gt": created_at_gt, "created_at_lt": created_at_lt, "include_archived": include_archived, "limit": limit, "page": page, "statuses": statuses, }, dream_list_params.DreamListParams, ), ), model=BetaDream, ) def archive( self, dream_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaDream: """ Archive a Dream Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not dream_id: raise ValueError(f"Expected a non-empty value for `dream_id` but received {dream_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["dreaming-2026-04-21"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "dreaming-2026-04-21", **(extra_headers or {})} return self._post( path_template("/v1/dreams/{dream_id}/archive?beta=true", dream_id=dream_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaDream, ) def cancel( self, dream_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaDream: """ Cancel a Dream Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not dream_id: raise ValueError(f"Expected a non-empty value for `dream_id` but received {dream_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["dreaming-2026-04-21"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "dreaming-2026-04-21", **(extra_headers or {})} return self._post( path_template("/v1/dreams/{dream_id}/cancel?beta=true", dream_id=dream_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaDream, ) class AsyncDreams(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncDreamsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncDreamsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncDreamsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncDreamsWithStreamingResponse(self) async def create( self, *, inputs: Iterable[BetaDreamInputParam], model: dream_create_params.Model, instructions: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaDream: """ Create a Dream Args: model: Model identifier and configuration applied to every pipeline stage. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["dreaming-2026-04-21"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "dreaming-2026-04-21", **(extra_headers or {})} return await self._post( "/v1/dreams?beta=true", body=await async_maybe_transform( { "inputs": inputs, "model": model, "instructions": instructions, }, dream_create_params.DreamCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaDream, ) async def retrieve( self, dream_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaDream: """ Get a Dream Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not dream_id: raise ValueError(f"Expected a non-empty value for `dream_id` but received {dream_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["dreaming-2026-04-21"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "dreaming-2026-04-21", **(extra_headers or {})} return await self._get( path_template("/v1/dreams/{dream_id}?beta=true", dream_id=dream_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaDream, ) def list( self, *, created_at_gt: Union[str, datetime] | Omit = omit, created_at_lt: Union[str, datetime] | Omit = omit, include_archived: bool | Omit = omit, limit: int | Omit = omit, page: str | Omit = omit, statuses: List[BetaDreamStatus] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[BetaDream, AsyncPageCursor[BetaDream]]: """ List Dreams Args: created_at_gt: Return dreams with `created_at` strictly after this timestamp (exclusive lower bound, RFC 3339). Unset applies no lower bound. created_at_lt: Return dreams with `created_at` strictly before this timestamp (exclusive upper bound, RFC 3339). Unset applies no upper bound. include_archived: Query parameter for include_archived limit: Query parameter for limit page: Query parameter for page statuses: Filter by lifecycle status. Repeat the parameter to match any of multiple statuses. Empty applies no status filter. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["dreaming-2026-04-21"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "dreaming-2026-04-21", **(extra_headers or {})} return self._get_api_list( "/v1/dreams?beta=true", page=AsyncPageCursor[BetaDream], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "created_at_gt": created_at_gt, "created_at_lt": created_at_lt, "include_archived": include_archived, "limit": limit, "page": page, "statuses": statuses, }, dream_list_params.DreamListParams, ), ), model=BetaDream, ) async def archive( self, dream_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaDream: """ Archive a Dream Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not dream_id: raise ValueError(f"Expected a non-empty value for `dream_id` but received {dream_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["dreaming-2026-04-21"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "dreaming-2026-04-21", **(extra_headers or {})} return await self._post( path_template("/v1/dreams/{dream_id}/archive?beta=true", dream_id=dream_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaDream, ) async def cancel( self, dream_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaDream: """ Cancel a Dream Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not dream_id: raise ValueError(f"Expected a non-empty value for `dream_id` but received {dream_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["dreaming-2026-04-21"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "dreaming-2026-04-21", **(extra_headers or {})} return await self._post( path_template("/v1/dreams/{dream_id}/cancel?beta=true", dream_id=dream_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaDream, ) class DreamsWithRawResponse: def __init__(self, dreams: Dreams) -> None: self._dreams = dreams self.create = _legacy_response.to_raw_response_wrapper( dreams.create, ) self.retrieve = _legacy_response.to_raw_response_wrapper( dreams.retrieve, ) self.list = _legacy_response.to_raw_response_wrapper( dreams.list, ) self.archive = _legacy_response.to_raw_response_wrapper( dreams.archive, ) self.cancel = _legacy_response.to_raw_response_wrapper( dreams.cancel, ) class AsyncDreamsWithRawResponse: def __init__(self, dreams: AsyncDreams) -> None: self._dreams = dreams self.create = _legacy_response.async_to_raw_response_wrapper( dreams.create, ) self.retrieve = _legacy_response.async_to_raw_response_wrapper( dreams.retrieve, ) self.list = _legacy_response.async_to_raw_response_wrapper( dreams.list, ) self.archive = _legacy_response.async_to_raw_response_wrapper( dreams.archive, ) self.cancel = _legacy_response.async_to_raw_response_wrapper( dreams.cancel, ) class DreamsWithStreamingResponse: def __init__(self, dreams: Dreams) -> None: self._dreams = dreams self.create = to_streamed_response_wrapper( dreams.create, ) self.retrieve = to_streamed_response_wrapper( dreams.retrieve, ) self.list = to_streamed_response_wrapper( dreams.list, ) self.archive = to_streamed_response_wrapper( dreams.archive, ) self.cancel = to_streamed_response_wrapper( dreams.cancel, ) class AsyncDreamsWithStreamingResponse: def __init__(self, dreams: AsyncDreams) -> None: self._dreams = dreams self.create = async_to_streamed_response_wrapper( dreams.create, ) self.retrieve = async_to_streamed_response_wrapper( dreams.retrieve, ) self.list = async_to_streamed_response_wrapper( dreams.list, ) self.archive = async_to_streamed_response_wrapper( dreams.archive, ) self.cancel = async_to_streamed_response_wrapper( dreams.cancel, ) anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/environments/000077500000000000000000000000001523216435200261765ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/environments/__init__.py000066400000000000000000000015361523216435200303140ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .work import ( Work, AsyncWork, WorkWithRawResponse, AsyncWorkWithRawResponse, WorkWithStreamingResponse, AsyncWorkWithStreamingResponse, ) from .environments import ( Environments, AsyncEnvironments, EnvironmentsWithRawResponse, AsyncEnvironmentsWithRawResponse, EnvironmentsWithStreamingResponse, AsyncEnvironmentsWithStreamingResponse, ) __all__ = [ "Work", "AsyncWork", "WorkWithRawResponse", "AsyncWorkWithRawResponse", "WorkWithStreamingResponse", "AsyncWorkWithStreamingResponse", "Environments", "AsyncEnvironments", "EnvironmentsWithRawResponse", "AsyncEnvironmentsWithRawResponse", "EnvironmentsWithStreamingResponse", "AsyncEnvironmentsWithStreamingResponse", ] anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/environments/environments.py000066400000000000000000001064611523216435200313070ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Optional from itertools import chain from typing_extensions import Literal import httpx from .... import _legacy_response from .work import ( Work, AsyncWork, WorkWithRawResponse, AsyncWorkWithRawResponse, WorkWithStreamingResponse, AsyncWorkWithStreamingResponse, ) from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ...._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ....pagination import SyncPageCursor, AsyncPageCursor from ....types.beta import environment_list_params, environment_create_params, environment_update_params from ...._base_client import AsyncPaginator, make_request_options from ....types.anthropic_beta_param import AnthropicBetaParam from ....types.beta.beta_environment import BetaEnvironment from ....types.beta.beta_environment_delete_response import BetaEnvironmentDeleteResponse __all__ = ["Environments", "AsyncEnvironments"] class Environments(SyncAPIResource): @cached_property def work(self) -> Work: return Work(self._client) @cached_property def with_raw_response(self) -> EnvironmentsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return EnvironmentsWithRawResponse(self) @cached_property def with_streaming_response(self) -> EnvironmentsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return EnvironmentsWithStreamingResponse(self) def create( self, *, name: str, config: Optional[environment_create_params.Config] | Omit = omit, description: Optional[str] | Omit = omit, metadata: Dict[str, str] | Omit = omit, scope: Optional[Literal["organization", "account"]] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaEnvironment: """ Create a new environment with the specified configuration. Args: name: Human-readable name for the environment config: Environment configuration description: Optional description of the environment metadata: User-provided metadata key-value pairs scope: The visibility scope for this environment. 'organization' makes the environment visible to all accounts. 'account' restricts visibility to the owning account only. Only applicable for self-hosted environments. If not specified, defaults based on organization type. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._post( "/v1/environments?beta=true", body=maybe_transform( { "name": name, "config": config, "description": description, "metadata": metadata, "scope": scope, }, environment_create_params.EnvironmentCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaEnvironment, ) def retrieve( self, environment_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaEnvironment: """ Retrieve a specific environment by ID. Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not environment_id: raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get( path_template("/v1/environments/{environment_id}?beta=true", environment_id=environment_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaEnvironment, ) def update( self, environment_id: str, *, config: Optional[environment_update_params.Config] | Omit = omit, description: Optional[str] | Omit = omit, metadata: Dict[str, Optional[str]] | Omit = omit, name: Optional[str] | Omit = omit, scope: Optional[Literal["organization", "account"]] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaEnvironment: """ Update an existing environment's configuration. Args: config: Updated environment configuration description: Updated description of the environment metadata: User-provided metadata key-value pairs. Set a value to null or empty string to delete the key. name: Updated name for the environment scope: The visibility scope for this environment. 'organization' makes the environment visible to all accounts. 'account' restricts visibility to the owning account only. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not environment_id: raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._post( path_template("/v1/environments/{environment_id}?beta=true", environment_id=environment_id), body=maybe_transform( { "config": config, "description": description, "metadata": metadata, "name": name, "scope": scope, }, environment_update_params.EnvironmentUpdateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaEnvironment, ) def list( self, *, include_archived: bool | Omit = omit, limit: int | Omit = omit, page: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncPageCursor[BetaEnvironment]: """ List environments with pagination support. Args: include_archived: Include archived environments in the response limit: Maximum number of environments to return page: Opaque cursor from previous response for pagination. Pass the `next_page` value from the previous response. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get_api_list( "/v1/environments?beta=true", page=SyncPageCursor[BetaEnvironment], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "include_archived": include_archived, "limit": limit, "page": page, }, environment_list_params.EnvironmentListParams, ), ), model=BetaEnvironment, ) def delete( self, environment_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaEnvironmentDeleteResponse: """Delete an environment by ID. Returns a confirmation of the deletion. Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not environment_id: raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._delete( path_template("/v1/environments/{environment_id}?beta=true", environment_id=environment_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaEnvironmentDeleteResponse, ) def archive( self, environment_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaEnvironment: """Archive an environment by ID. Archived environments cannot be used to create new sessions. Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not environment_id: raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._post( path_template("/v1/environments/{environment_id}/archive?beta=true", environment_id=environment_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaEnvironment, ) class AsyncEnvironments(AsyncAPIResource): @cached_property def work(self) -> AsyncWork: return AsyncWork(self._client) @cached_property def with_raw_response(self) -> AsyncEnvironmentsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncEnvironmentsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncEnvironmentsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncEnvironmentsWithStreamingResponse(self) async def create( self, *, name: str, config: Optional[environment_create_params.Config] | Omit = omit, description: Optional[str] | Omit = omit, metadata: Dict[str, str] | Omit = omit, scope: Optional[Literal["organization", "account"]] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaEnvironment: """ Create a new environment with the specified configuration. Args: name: Human-readable name for the environment config: Environment configuration description: Optional description of the environment metadata: User-provided metadata key-value pairs scope: The visibility scope for this environment. 'organization' makes the environment visible to all accounts. 'account' restricts visibility to the owning account only. Only applicable for self-hosted environments. If not specified, defaults based on organization type. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._post( "/v1/environments?beta=true", body=await async_maybe_transform( { "name": name, "config": config, "description": description, "metadata": metadata, "scope": scope, }, environment_create_params.EnvironmentCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaEnvironment, ) async def retrieve( self, environment_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaEnvironment: """ Retrieve a specific environment by ID. Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not environment_id: raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._get( path_template("/v1/environments/{environment_id}?beta=true", environment_id=environment_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaEnvironment, ) async def update( self, environment_id: str, *, config: Optional[environment_update_params.Config] | Omit = omit, description: Optional[str] | Omit = omit, metadata: Dict[str, Optional[str]] | Omit = omit, name: Optional[str] | Omit = omit, scope: Optional[Literal["organization", "account"]] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaEnvironment: """ Update an existing environment's configuration. Args: config: Updated environment configuration description: Updated description of the environment metadata: User-provided metadata key-value pairs. Set a value to null or empty string to delete the key. name: Updated name for the environment scope: The visibility scope for this environment. 'organization' makes the environment visible to all accounts. 'account' restricts visibility to the owning account only. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not environment_id: raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._post( path_template("/v1/environments/{environment_id}?beta=true", environment_id=environment_id), body=await async_maybe_transform( { "config": config, "description": description, "metadata": metadata, "name": name, "scope": scope, }, environment_update_params.EnvironmentUpdateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaEnvironment, ) def list( self, *, include_archived: bool | Omit = omit, limit: int | Omit = omit, page: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[BetaEnvironment, AsyncPageCursor[BetaEnvironment]]: """ List environments with pagination support. Args: include_archived: Include archived environments in the response limit: Maximum number of environments to return page: Opaque cursor from previous response for pagination. Pass the `next_page` value from the previous response. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get_api_list( "/v1/environments?beta=true", page=AsyncPageCursor[BetaEnvironment], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "include_archived": include_archived, "limit": limit, "page": page, }, environment_list_params.EnvironmentListParams, ), ), model=BetaEnvironment, ) async def delete( self, environment_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaEnvironmentDeleteResponse: """Delete an environment by ID. Returns a confirmation of the deletion. Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not environment_id: raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._delete( path_template("/v1/environments/{environment_id}?beta=true", environment_id=environment_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaEnvironmentDeleteResponse, ) async def archive( self, environment_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaEnvironment: """Archive an environment by ID. Archived environments cannot be used to create new sessions. Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not environment_id: raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._post( path_template("/v1/environments/{environment_id}/archive?beta=true", environment_id=environment_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaEnvironment, ) class EnvironmentsWithRawResponse: def __init__(self, environments: Environments) -> None: self._environments = environments self.create = _legacy_response.to_raw_response_wrapper( environments.create, ) self.retrieve = _legacy_response.to_raw_response_wrapper( environments.retrieve, ) self.update = _legacy_response.to_raw_response_wrapper( environments.update, ) self.list = _legacy_response.to_raw_response_wrapper( environments.list, ) self.delete = _legacy_response.to_raw_response_wrapper( environments.delete, ) self.archive = _legacy_response.to_raw_response_wrapper( environments.archive, ) @cached_property def work(self) -> WorkWithRawResponse: return WorkWithRawResponse(self._environments.work) class AsyncEnvironmentsWithRawResponse: def __init__(self, environments: AsyncEnvironments) -> None: self._environments = environments self.create = _legacy_response.async_to_raw_response_wrapper( environments.create, ) self.retrieve = _legacy_response.async_to_raw_response_wrapper( environments.retrieve, ) self.update = _legacy_response.async_to_raw_response_wrapper( environments.update, ) self.list = _legacy_response.async_to_raw_response_wrapper( environments.list, ) self.delete = _legacy_response.async_to_raw_response_wrapper( environments.delete, ) self.archive = _legacy_response.async_to_raw_response_wrapper( environments.archive, ) @cached_property def work(self) -> AsyncWorkWithRawResponse: return AsyncWorkWithRawResponse(self._environments.work) class EnvironmentsWithStreamingResponse: def __init__(self, environments: Environments) -> None: self._environments = environments self.create = to_streamed_response_wrapper( environments.create, ) self.retrieve = to_streamed_response_wrapper( environments.retrieve, ) self.update = to_streamed_response_wrapper( environments.update, ) self.list = to_streamed_response_wrapper( environments.list, ) self.delete = to_streamed_response_wrapper( environments.delete, ) self.archive = to_streamed_response_wrapper( environments.archive, ) @cached_property def work(self) -> WorkWithStreamingResponse: return WorkWithStreamingResponse(self._environments.work) class AsyncEnvironmentsWithStreamingResponse: def __init__(self, environments: AsyncEnvironments) -> None: self._environments = environments self.create = async_to_streamed_response_wrapper( environments.create, ) self.retrieve = async_to_streamed_response_wrapper( environments.retrieve, ) self.update = async_to_streamed_response_wrapper( environments.update, ) self.list = async_to_streamed_response_wrapper( environments.list, ) self.delete = async_to_streamed_response_wrapper( environments.delete, ) self.archive = async_to_streamed_response_wrapper( environments.archive, ) @cached_property def work(self) -> AsyncWorkWithStreamingResponse: return AsyncWorkWithStreamingResponse(self._environments.work) anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/environments/work.py000066400000000000000000001650021523216435200275360ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os from typing import TYPE_CHECKING, Dict, List, Optional, cast from itertools import chain import httpx if TYPE_CHECKING: from collections.abc import AsyncIterator from ...._client import AsyncAnthropic from ....lib.environments._worker import EnvironmentWorker, EnvironmentWorkerTools from .... import _legacy_response from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ...._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ....pagination import SyncPageCursor, AsyncPageCursor from ...._base_client import AsyncPaginator, make_request_options from ....types.beta.environments import ( work_list_params, work_poll_params, work_stop_params, work_update_params, work_heartbeat_params, ) from ....types.anthropic_beta_param import AnthropicBetaParam from ....types.beta.environments.beta_self_hosted_work import BetaSelfHostedWork from ....types.beta.environments.beta_self_hosted_work_queue_stats import BetaSelfHostedWorkQueueStats from ....types.beta.environments.beta_self_hosted_work_heartbeat_response import BetaSelfHostedWorkHeartbeatResponse __all__ = ["Work", "AsyncWork"] class Work(SyncAPIResource): @cached_property def with_raw_response(self) -> WorkWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return WorkWithRawResponse(self) @cached_property def with_streaming_response(self) -> WorkWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return WorkWithStreamingResponse(self) def retrieve( self, work_id: str, *, environment_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaSelfHostedWork: """ Note: these endpoints are called automatically by the pre-built environment worker provided in the SDKs and CLI, for orchestrating sessions with self-hosted sandbox environments. They are included here as a reference; you do not need to invoke them directly. Retrieve detailed information about a specific work item. Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not environment_id: raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") if not work_id: raise ValueError(f"Expected a non-empty value for `work_id` but received {work_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get( path_template( "/v1/environments/{environment_id}/work/{work_id}?beta=true", environment_id=environment_id, work_id=work_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaSelfHostedWork, ) def update( self, work_id: str, *, environment_id: str, metadata: Dict[str, Optional[str]], betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaSelfHostedWork: """ Note: these endpoints are called automatically by the pre-built environment worker provided in the SDKs and CLI, for orchestrating sessions with self-hosted sandbox environments. They are included here as a reference; you do not need to invoke them directly. Update work item metadata with merge semantics. Args: metadata: Metadata patch. Set a key to a string to upsert it, or to null to delete it. Omit the field to preserve existing metadata. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not environment_id: raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") if not work_id: raise ValueError(f"Expected a non-empty value for `work_id` but received {work_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._post( path_template( "/v1/environments/{environment_id}/work/{work_id}?beta=true", environment_id=environment_id, work_id=work_id, ), body=maybe_transform({"metadata": metadata}, work_update_params.WorkUpdateParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaSelfHostedWork, ) def list( self, environment_id: str, *, limit: int | Omit = omit, page: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncPageCursor[BetaSelfHostedWork]: """ Note: these endpoints are called automatically by the pre-built environment worker provided in the SDKs and CLI, for orchestrating sessions with self-hosted sandbox environments. They are included here as a reference; you do not need to invoke them directly. List work items in an environment. Args: limit: Maximum number of work items to return page: Opaque cursor from previous response for pagination betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not environment_id: raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get_api_list( path_template("/v1/environments/{environment_id}/work?beta=true", environment_id=environment_id), page=SyncPageCursor[BetaSelfHostedWork], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "limit": limit, "page": page, }, work_list_params.WorkListParams, ), ), model=BetaSelfHostedWork, ) def ack( self, work_id: str, *, environment_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaSelfHostedWork: """ Note: these endpoints are called automatically by the pre-built environment worker provided in the SDKs and CLI, for orchestrating sessions with self-hosted sandbox environments. They are included here as a reference; you do not need to invoke them directly. Acknowledge receipt of a work item, transitioning it from 'queued' to 'starting' and removing it from the queue. Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not environment_id: raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") if not work_id: raise ValueError(f"Expected a non-empty value for `work_id` but received {work_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._post( path_template( "/v1/environments/{environment_id}/work/{work_id}/ack?beta=true", environment_id=environment_id, work_id=work_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaSelfHostedWork, ) def heartbeat( self, work_id: str, *, environment_id: str, desired_ttl_seconds: Optional[int] | Omit = omit, expected_last_heartbeat: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaSelfHostedWorkHeartbeatResponse: """ Note: these endpoints are called automatically by the pre-built environment worker provided in the SDKs and CLI, for orchestrating sessions with self-hosted sandbox environments. They are included here as a reference; you do not need to invoke them directly. Record a heartbeat for a work item to maintain the lease. Args: desired_ttl_seconds: Desired TTL in seconds expected_last_heartbeat: Expected last_heartbeat for conditional update (optimistic concurrency). Use literal 'NO_HEARTBEAT' to claim an unclaimed lease (first heartbeat). For subsequent heartbeats, echo the server's previous last_heartbeat value exactly. Returns 412 Precondition Failed if the actual value doesn't match. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not environment_id: raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") if not work_id: raise ValueError(f"Expected a non-empty value for `work_id` but received {work_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._post( path_template( "/v1/environments/{environment_id}/work/{work_id}/heartbeat?beta=true", environment_id=environment_id, work_id=work_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "desired_ttl_seconds": desired_ttl_seconds, "expected_last_heartbeat": expected_last_heartbeat, }, work_heartbeat_params.WorkHeartbeatParams, ), ), cast_to=BetaSelfHostedWorkHeartbeatResponse, ) def poll( self, environment_id: str, *, block_ms: Optional[int] | Omit = omit, reclaim_older_than_ms: Optional[int] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, anthropic_worker_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Optional[BetaSelfHostedWork]: """ Note: these endpoints are called automatically by the pre-built environment worker provided in the SDKs and CLI, for orchestrating sessions with self-hosted sandbox environments. They are included here as a reference; you do not need to invoke them directly. Long poll for work items in the queue. Args: block_ms: How long to wait for work to arrive before returning. Must be 1-999 in milliseconds. Defaults to non-blocking (returns immediately if no work is available). reclaim_older_than_ms: Reclaim unacknowledged work items older than this many milliseconds. If omitted, uses the default (5000ms). betas: Optional header to specify the beta version(s) you want to use. anthropic_worker_id: Unique identifier for the specific worker polling, used to track aggregated environment-level work metrics in Console extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not environment_id: raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given, "Anthropic-Worker-ID": anthropic_worker_id, } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get( path_template("/v1/environments/{environment_id}/work/poll?beta=true", environment_id=environment_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "block_ms": block_ms, "reclaim_older_than_ms": reclaim_older_than_ms, }, work_poll_params.WorkPollParams, ), ), cast_to=BetaSelfHostedWork, ) def stats( self, environment_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaSelfHostedWorkQueueStats: """ Get statistics about the work queue for an environment. Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not environment_id: raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get( path_template("/v1/environments/{environment_id}/work/stats?beta=true", environment_id=environment_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaSelfHostedWorkQueueStats, ) def stop( self, work_id: str, *, environment_id: str, force: bool | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaSelfHostedWork: """ Note: these endpoints are called automatically by the pre-built environment worker provided in the SDKs and CLI, for orchestrating sessions with self-hosted sandbox environments. They are included here as a reference; you do not need to invoke them directly. Stop a work item, initiating graceful or forced shutdown. Args: force: If true, immediately stop work without graceful shutdown betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not environment_id: raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") if not work_id: raise ValueError(f"Expected a non-empty value for `work_id` but received {work_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._post( path_template( "/v1/environments/{environment_id}/work/{work_id}/stop?beta=true", environment_id=environment_id, work_id=work_id, ), body=maybe_transform({"force": force}, work_stop_params.WorkStopParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaSelfHostedWork, ) class AsyncWork(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncWorkWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncWorkWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncWorkWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncWorkWithStreamingResponse(self) async def retrieve( self, work_id: str, *, environment_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaSelfHostedWork: """ Note: these endpoints are called automatically by the pre-built environment worker provided in the SDKs and CLI, for orchestrating sessions with self-hosted sandbox environments. They are included here as a reference; you do not need to invoke them directly. Retrieve detailed information about a specific work item. Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not environment_id: raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") if not work_id: raise ValueError(f"Expected a non-empty value for `work_id` but received {work_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._get( path_template( "/v1/environments/{environment_id}/work/{work_id}?beta=true", environment_id=environment_id, work_id=work_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaSelfHostedWork, ) async def update( self, work_id: str, *, environment_id: str, metadata: Dict[str, Optional[str]], betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaSelfHostedWork: """ Note: these endpoints are called automatically by the pre-built environment worker provided in the SDKs and CLI, for orchestrating sessions with self-hosted sandbox environments. They are included here as a reference; you do not need to invoke them directly. Update work item metadata with merge semantics. Args: metadata: Metadata patch. Set a key to a string to upsert it, or to null to delete it. Omit the field to preserve existing metadata. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not environment_id: raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") if not work_id: raise ValueError(f"Expected a non-empty value for `work_id` but received {work_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._post( path_template( "/v1/environments/{environment_id}/work/{work_id}?beta=true", environment_id=environment_id, work_id=work_id, ), body=await async_maybe_transform({"metadata": metadata}, work_update_params.WorkUpdateParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaSelfHostedWork, ) def list( self, environment_id: str, *, limit: int | Omit = omit, page: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[BetaSelfHostedWork, AsyncPageCursor[BetaSelfHostedWork]]: """ Note: these endpoints are called automatically by the pre-built environment worker provided in the SDKs and CLI, for orchestrating sessions with self-hosted sandbox environments. They are included here as a reference; you do not need to invoke them directly. List work items in an environment. Args: limit: Maximum number of work items to return page: Opaque cursor from previous response for pagination betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not environment_id: raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get_api_list( path_template("/v1/environments/{environment_id}/work?beta=true", environment_id=environment_id), page=AsyncPageCursor[BetaSelfHostedWork], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "limit": limit, "page": page, }, work_list_params.WorkListParams, ), ), model=BetaSelfHostedWork, ) async def ack( self, work_id: str, *, environment_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaSelfHostedWork: """ Note: these endpoints are called automatically by the pre-built environment worker provided in the SDKs and CLI, for orchestrating sessions with self-hosted sandbox environments. They are included here as a reference; you do not need to invoke them directly. Acknowledge receipt of a work item, transitioning it from 'queued' to 'starting' and removing it from the queue. Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not environment_id: raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") if not work_id: raise ValueError(f"Expected a non-empty value for `work_id` but received {work_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._post( path_template( "/v1/environments/{environment_id}/work/{work_id}/ack?beta=true", environment_id=environment_id, work_id=work_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaSelfHostedWork, ) async def heartbeat( self, work_id: str, *, environment_id: str, desired_ttl_seconds: Optional[int] | Omit = omit, expected_last_heartbeat: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaSelfHostedWorkHeartbeatResponse: """ Note: these endpoints are called automatically by the pre-built environment worker provided in the SDKs and CLI, for orchestrating sessions with self-hosted sandbox environments. They are included here as a reference; you do not need to invoke them directly. Record a heartbeat for a work item to maintain the lease. Args: desired_ttl_seconds: Desired TTL in seconds expected_last_heartbeat: Expected last_heartbeat for conditional update (optimistic concurrency). Use literal 'NO_HEARTBEAT' to claim an unclaimed lease (first heartbeat). For subsequent heartbeats, echo the server's previous last_heartbeat value exactly. Returns 412 Precondition Failed if the actual value doesn't match. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not environment_id: raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") if not work_id: raise ValueError(f"Expected a non-empty value for `work_id` but received {work_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._post( path_template( "/v1/environments/{environment_id}/work/{work_id}/heartbeat?beta=true", environment_id=environment_id, work_id=work_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=await async_maybe_transform( { "desired_ttl_seconds": desired_ttl_seconds, "expected_last_heartbeat": expected_last_heartbeat, }, work_heartbeat_params.WorkHeartbeatParams, ), ), cast_to=BetaSelfHostedWorkHeartbeatResponse, ) async def poll( self, environment_id: str, *, block_ms: Optional[int] | Omit = omit, reclaim_older_than_ms: Optional[int] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, anthropic_worker_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Optional[BetaSelfHostedWork]: """ Note: these endpoints are called automatically by the pre-built environment worker provided in the SDKs and CLI, for orchestrating sessions with self-hosted sandbox environments. They are included here as a reference; you do not need to invoke them directly. Long poll for work items in the queue. Args: block_ms: How long to wait for work to arrive before returning. Must be 1-999 in milliseconds. Defaults to non-blocking (returns immediately if no work is available). reclaim_older_than_ms: Reclaim unacknowledged work items older than this many milliseconds. If omitted, uses the default (5000ms). betas: Optional header to specify the beta version(s) you want to use. anthropic_worker_id: Unique identifier for the specific worker polling, used to track aggregated environment-level work metrics in Console extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not environment_id: raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given, "Anthropic-Worker-ID": anthropic_worker_id, } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._get( path_template("/v1/environments/{environment_id}/work/poll?beta=true", environment_id=environment_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=await async_maybe_transform( { "block_ms": block_ms, "reclaim_older_than_ms": reclaim_older_than_ms, }, work_poll_params.WorkPollParams, ), ), cast_to=BetaSelfHostedWork, ) async def stats( self, environment_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaSelfHostedWorkQueueStats: """ Get statistics about the work queue for an environment. Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not environment_id: raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._get( path_template("/v1/environments/{environment_id}/work/stats?beta=true", environment_id=environment_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaSelfHostedWorkQueueStats, ) async def stop( self, work_id: str, *, environment_id: str, force: bool | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaSelfHostedWork: """ Note: these endpoints are called automatically by the pre-built environment worker provided in the SDKs and CLI, for orchestrating sessions with self-hosted sandbox environments. They are included here as a reference; you do not need to invoke them directly. Stop a work item, initiating graceful or forced shutdown. Args: force: If true, immediately stop work without graceful shutdown betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not environment_id: raise ValueError(f"Expected a non-empty value for `environment_id` but received {environment_id!r}") if not work_id: raise ValueError(f"Expected a non-empty value for `work_id` but received {work_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._post( path_template( "/v1/environments/{environment_id}/work/{work_id}/stop?beta=true", environment_id=environment_id, work_id=work_id, ), body=await async_maybe_transform({"force": force}, work_stop_params.WorkStopParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaSelfHostedWork, ) def poller( self, *, environment_id: str, environment_key: str, worker_id: str | None = None, block_ms: int | None | NotGiven = not_given, reclaim_older_than_ms: int | None = None, drain: bool = False, auto_stop: bool = True, extra_headers: Headers | None = None, ) -> AsyncIterator[BetaSelfHostedWork]: """Async-iterate work items claimed from a self-hosted environment. Each yielded item has been ack'd. The environment key authenticates the poll, ack, and stop calls via a scoped sub-client (built once per call). Async only — available on :class:`~anthropic.AsyncAnthropic` (the sync client does not expose ``poller``). With the defaults this loops forever and calls ``stop`` after the consuming ``async for`` body returns or raises (long-running runner shape). Pass ``drain=True, auto_stop=False`` to drain whatever is queued and return without owning the stop call (webhook-dispatch shape — each item is handed off to another process that calls ``stop`` when done). Args: environment_id: The self-hosted environment to claim work from. environment_key: The environment key — used as the Bearer credential on the scoped sub-client that issues poll / ack / stop requests. worker_id: Optional identifier sent on each poll. Defaults to a unique, hostname-prefixed id. block_ms: How long the server should hold an empty poll open before returning (long-poll). Server caps this at 999. Defaults to ``POLL_BLOCK_MS`` (999) when not given. Pass ``None`` to omit for a non-blocking poll — the server rejects ``0``. reclaim_older_than_ms: Reclaim un-ack'd work older than this many ms. Forwarded to the underlying poll request. drain: When True, return after the first empty poll instead of sleeping and re-polling. auto_stop: When True (default), call ``stop`` after the consumer's loop body completes. Set False when handing items off to another process that owns the stop call. extra_headers: Optional headers passed through per request on the poll / ack / stop calls. They are threaded into each call's ``extra_headers=`` and never assigned onto the client, so client state is not mutated. Auth and ``x-stainless-helper`` are supplied by the scoped sub-client built here (and the parent client's ``default_headers`` propagate via its ``client.copy()``); a header given here overrides the scoped client's same-named default for that request, so use it for caller passthrough (e.g. trace ids), not to set auth. """ # POLL_BLOCK_MS is resolved here, not used as a literal signature # default: importing _poller at module load would form an import cycle # (_poller imports this module) and pull the host-only environment lib # into ``import anthropic``. The sentinel keeps a single source of truth # for the default so it can't drift from the constant. from ....lib._scoped_client import _copy_client_with_bearer_auth from ....lib.environments._poller import POLL_BLOCK_MS, aiter_work if not is_given(block_ms): block_ms = POLL_BLOCK_MS scoped = _copy_client_with_bearer_auth( cast("AsyncAnthropic", self._client), auth_token=environment_key, helper="environments-work-poller", ) return aiter_work( scoped.beta.environments.work, environment_id=environment_id, worker_id=worker_id, block_ms=block_ms, reclaim_older_than_ms=reclaim_older_than_ms, drain=drain, auto_stop=auto_stop, extra_headers=extra_headers, ) def worker( self, *, environment_id: str | None = None, environment_key: str | None = None, tools: EnvironmentWorkerTools | None = None, workdir: str | os.PathLike[str] | None = None, unrestricted_paths: bool = False, max_file_bytes: int | None | NotGiven = not_given, max_idle: float | None | NotGiven = not_given, worker_id: str | None = None, extra_headers: Headers | None = None, ) -> EnvironmentWorker: """Build an :class:`~anthropic.lib.environments.EnvironmentWorker` bound to this async client. The full worker: it polls the environment for work, and for each claimed session sets up the workdir + downloads the session agent's skills, runs the given ``tools`` against the session's tool-call events while heartbeating the work-item lease, force-stops the work on exit, and loops. Composed from this resource's :meth:`poller` and the per-session session tool runner. ``EnvironmentWorker`` is async only — its ``run`` / ``handle_item`` coroutines need an event loop. With this :class:`~anthropic.AsyncAnthropic` client the returned worker is ready to ``await worker.run()`` (long-running poll loop) or ``await worker.handle_item()`` (single already-claimed work item). It can also be constructed directly: ``EnvironmentWorker(client, ...)``. Args: environment_id: The self-hosted environment to poll for work. Required by ``EnvironmentWorker.run``; not used by ``EnvironmentWorker.handle_item``. environment_key: The environment key — the worker's single credential, used as Bearer auth on the control-plane and session-level calls. tools: Tools to expose to each claimed session. Either a fixed list or a factory invoked once per session with that session's ``AgentToolContext``. Defaults to ``beta_agent_toolset_20260401(env)``. workdir: Base directory for the per-session ``AgentToolContext``. Defaults to ``os.getcwd()`` captured when the worker is constructed (TS parity: ``process.cwd()`` at construction). unrestricted_paths: Forwarded to the per-session ``AgentToolContext``. max_file_bytes: Forwarded to the per-session ``AgentToolContext`` — the size cap (bytes) for the ``read``/``edit`` tools. ``not_given`` (default) uses the built-in 256 KiB cap; a positive int sets a custom cap; ``None`` disables the cap. max_idle: Seconds to keep running after the session goes idle with ``stop_reason`` ``end_turn``. Defaults to ``DEFAULT_MAX_IDLE`` (60s) when not given. ``None`` disables it. worker_id: Optional identifier sent on each poll. Defaults to a unique, hostname-prefixed id. extra_headers: Optional headers passed through per request on every call the worker makes (poll / ack / stop / heartbeat and the session tool runner's event stream / list / send). They are threaded into each call's ``extra_headers=`` and never assigned onto the client, so client state is not mutated. Auth and ``x-stainless-helper`` are supplied by the worker's scoped sub-clients (and the parent client's ``default_headers`` propagate via their ``client.copy()``); a header given here overrides a scoped client's same-named default for that request, so use it for caller passthrough (e.g. trace ids), not to set auth. """ # DEFAULT_MAX_IDLE resolved here rather than as a literal signature # default so the value can't drift from the constant; the lazy import # also keeps the host-only environment lib out of ``import anthropic``. from ....lib.environments._worker import EnvironmentWorker from ....lib.tools._beta_session_runner import DEFAULT_MAX_IDLE if not is_given(max_idle): max_idle = DEFAULT_MAX_IDLE return EnvironmentWorker( cast("AsyncAnthropic", self._client), environment_id=environment_id, environment_key=environment_key, tools=tools, workdir=workdir, unrestricted_paths=unrestricted_paths, max_file_bytes=max_file_bytes, max_idle=max_idle, worker_id=worker_id, extra_headers=extra_headers, ) class WorkWithRawResponse: def __init__(self, work: Work) -> None: self._work = work self.retrieve = _legacy_response.to_raw_response_wrapper( work.retrieve, ) self.update = _legacy_response.to_raw_response_wrapper( work.update, ) self.list = _legacy_response.to_raw_response_wrapper( work.list, ) self.ack = _legacy_response.to_raw_response_wrapper( work.ack, ) self.heartbeat = _legacy_response.to_raw_response_wrapper( work.heartbeat, ) self.poll = _legacy_response.to_raw_response_wrapper( work.poll, ) self.stats = _legacy_response.to_raw_response_wrapper( work.stats, ) self.stop = _legacy_response.to_raw_response_wrapper( work.stop, ) class AsyncWorkWithRawResponse: def __init__(self, work: AsyncWork) -> None: self._work = work self.retrieve = _legacy_response.async_to_raw_response_wrapper( work.retrieve, ) self.update = _legacy_response.async_to_raw_response_wrapper( work.update, ) self.list = _legacy_response.async_to_raw_response_wrapper( work.list, ) self.ack = _legacy_response.async_to_raw_response_wrapper( work.ack, ) self.heartbeat = _legacy_response.async_to_raw_response_wrapper( work.heartbeat, ) self.poll = _legacy_response.async_to_raw_response_wrapper( work.poll, ) self.stats = _legacy_response.async_to_raw_response_wrapper( work.stats, ) self.stop = _legacy_response.async_to_raw_response_wrapper( work.stop, ) class WorkWithStreamingResponse: def __init__(self, work: Work) -> None: self._work = work self.retrieve = to_streamed_response_wrapper( work.retrieve, ) self.update = to_streamed_response_wrapper( work.update, ) self.list = to_streamed_response_wrapper( work.list, ) self.ack = to_streamed_response_wrapper( work.ack, ) self.heartbeat = to_streamed_response_wrapper( work.heartbeat, ) self.poll = to_streamed_response_wrapper( work.poll, ) self.stats = to_streamed_response_wrapper( work.stats, ) self.stop = to_streamed_response_wrapper( work.stop, ) class AsyncWorkWithStreamingResponse: def __init__(self, work: AsyncWork) -> None: self._work = work self.retrieve = async_to_streamed_response_wrapper( work.retrieve, ) self.update = async_to_streamed_response_wrapper( work.update, ) self.list = async_to_streamed_response_wrapper( work.list, ) self.ack = async_to_streamed_response_wrapper( work.ack, ) self.heartbeat = async_to_streamed_response_wrapper( work.heartbeat, ) self.poll = async_to_streamed_response_wrapper( work.poll, ) self.stats = async_to_streamed_response_wrapper( work.stats, ) self.stop = async_to_streamed_response_wrapper( work.stop, ) anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/files.py000066400000000000000000000662241523216435200251350ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Mapping, cast from itertools import chain import httpx from ... import _legacy_response from ..._files import deepcopy_with_paths from ..._types import Body, Omit, Query, Headers, NotGiven, FileTypes, omit, not_given from ..._utils import is_given, extract_files, path_template, maybe_transform, strip_not_given, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( BinaryAPIResponse, AsyncBinaryAPIResponse, StreamedBinaryAPIResponse, AsyncStreamedBinaryAPIResponse, to_streamed_response_wrapper, to_custom_raw_response_wrapper, async_to_streamed_response_wrapper, to_custom_streamed_response_wrapper, async_to_custom_raw_response_wrapper, async_to_custom_streamed_response_wrapper, ) from ...pagination import SyncPage, AsyncPage from ...types.beta import file_list_params, file_upload_params from ..._base_client import ( AsyncPaginator, merge_headers, make_request_options, ) from ...lib._stainless_helpers import stainless_helper_header_from_file as _stainless_helper_header_from_file from ...types.beta.deleted_file import DeletedFile from ...types.beta.file_metadata import FileMetadata from ...types.anthropic_beta_param import AnthropicBetaParam __all__ = ["Files", "AsyncFiles"] class Files(SyncAPIResource): @cached_property def with_raw_response(self) -> FilesWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return FilesWithRawResponse(self) @cached_property def with_streaming_response(self) -> FilesWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return FilesWithStreamingResponse(self) def list( self, *, after_id: str | Omit = omit, before_id: str | Omit = omit, limit: int | Omit = omit, scope_id: str | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncPage[FileMetadata]: """List Files Args: after_id: ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately after this object. before_id: ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately before this object. limit: Number of items to return per page. Defaults to `20`. Ranges from `1` to `1000`. scope_id: Filter by scope ID. Only returns files associated with the specified scope (e.g., a session ID). betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["files-api-2025-04-14"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "files-api-2025-04-14", **(extra_headers or {})} return self._get_api_list( "/v1/files?beta=true", page=SyncPage[FileMetadata], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "after_id": after_id, "before_id": before_id, "limit": limit, "scope_id": scope_id, }, file_list_params.FileListParams, ), ), model=FileMetadata, ) def delete( self, file_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> DeletedFile: """ Delete File Args: file_id: ID of the File. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not file_id: raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["files-api-2025-04-14"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "files-api-2025-04-14", **(extra_headers or {})} return self._delete( path_template("/v1/files/{file_id}?beta=true", file_id=file_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=DeletedFile, ) def download( self, file_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BinaryAPIResponse: """ Download File Args: file_id: ID of the File. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not file_id: raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") extra_headers = {"Accept": "application/binary", **(extra_headers or {})} extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["files-api-2025-04-14"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "files-api-2025-04-14", **(extra_headers or {})} return self._get( path_template("/v1/files/{file_id}/content?beta=true", file_id=file_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BinaryAPIResponse, ) def retrieve_metadata( self, file_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FileMetadata: """ Get File Metadata Args: file_id: ID of the File. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not file_id: raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["files-api-2025-04-14"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "files-api-2025-04-14", **(extra_headers or {})} return self._get( path_template("/v1/files/{file_id}?beta=true", file_id=file_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=FileMetadata, ) def upload( self, *, file: FileTypes, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FileMetadata: """ Upload File Args: file: The file to upload betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["files-api-2025-04-14"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "files-api-2025-04-14", **(extra_headers or {})} extra_headers = merge_headers(_stainless_helper_header_from_file(file), extra_headers) body = deepcopy_with_paths({"file": file}, [["file"]]) files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers["Content-Type"] = "multipart/form-data" return self._post( "/v1/files?beta=true", body=maybe_transform(body, file_upload_params.FileUploadParams), files=files, options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=FileMetadata, ) class AsyncFiles(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncFilesWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncFilesWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncFilesWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncFilesWithStreamingResponse(self) def list( self, *, after_id: str | Omit = omit, before_id: str | Omit = omit, limit: int | Omit = omit, scope_id: str | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[FileMetadata, AsyncPage[FileMetadata]]: """List Files Args: after_id: ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately after this object. before_id: ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately before this object. limit: Number of items to return per page. Defaults to `20`. Ranges from `1` to `1000`. scope_id: Filter by scope ID. Only returns files associated with the specified scope (e.g., a session ID). betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["files-api-2025-04-14"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "files-api-2025-04-14", **(extra_headers or {})} return self._get_api_list( "/v1/files?beta=true", page=AsyncPage[FileMetadata], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "after_id": after_id, "before_id": before_id, "limit": limit, "scope_id": scope_id, }, file_list_params.FileListParams, ), ), model=FileMetadata, ) async def delete( self, file_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> DeletedFile: """ Delete File Args: file_id: ID of the File. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not file_id: raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["files-api-2025-04-14"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "files-api-2025-04-14", **(extra_headers or {})} return await self._delete( path_template("/v1/files/{file_id}?beta=true", file_id=file_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=DeletedFile, ) async def download( self, file_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncBinaryAPIResponse: """ Download File Args: file_id: ID of the File. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not file_id: raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") extra_headers = {"Accept": "application/binary", **(extra_headers or {})} extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["files-api-2025-04-14"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "files-api-2025-04-14", **(extra_headers or {})} return await self._get( path_template("/v1/files/{file_id}/content?beta=true", file_id=file_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=AsyncBinaryAPIResponse, ) async def retrieve_metadata( self, file_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FileMetadata: """ Get File Metadata Args: file_id: ID of the File. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not file_id: raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["files-api-2025-04-14"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "files-api-2025-04-14", **(extra_headers or {})} return await self._get( path_template("/v1/files/{file_id}?beta=true", file_id=file_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=FileMetadata, ) async def upload( self, *, file: FileTypes, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> FileMetadata: """ Upload File Args: file: The file to upload betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["files-api-2025-04-14"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "files-api-2025-04-14", **(extra_headers or {})} extra_headers = merge_headers(_stainless_helper_header_from_file(file), extra_headers) body = deepcopy_with_paths({"file": file}, [["file"]]) files = extract_files(cast(Mapping[str, object], body), paths=[["file"]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers["Content-Type"] = "multipart/form-data" return await self._post( "/v1/files?beta=true", body=await async_maybe_transform(body, file_upload_params.FileUploadParams), files=files, options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=FileMetadata, ) class FilesWithRawResponse: def __init__(self, files: Files) -> None: self._files = files self.list = _legacy_response.to_raw_response_wrapper( files.list, ) self.delete = _legacy_response.to_raw_response_wrapper( files.delete, ) self.download = to_custom_raw_response_wrapper( files.download, BinaryAPIResponse, ) self.retrieve_metadata = _legacy_response.to_raw_response_wrapper( files.retrieve_metadata, ) self.upload = _legacy_response.to_raw_response_wrapper( files.upload, ) class AsyncFilesWithRawResponse: def __init__(self, files: AsyncFiles) -> None: self._files = files self.list = _legacy_response.async_to_raw_response_wrapper( files.list, ) self.delete = _legacy_response.async_to_raw_response_wrapper( files.delete, ) self.download = async_to_custom_raw_response_wrapper( files.download, AsyncBinaryAPIResponse, ) self.retrieve_metadata = _legacy_response.async_to_raw_response_wrapper( files.retrieve_metadata, ) self.upload = _legacy_response.async_to_raw_response_wrapper( files.upload, ) class FilesWithStreamingResponse: def __init__(self, files: Files) -> None: self._files = files self.list = to_streamed_response_wrapper( files.list, ) self.delete = to_streamed_response_wrapper( files.delete, ) self.download = to_custom_streamed_response_wrapper( files.download, StreamedBinaryAPIResponse, ) self.retrieve_metadata = to_streamed_response_wrapper( files.retrieve_metadata, ) self.upload = to_streamed_response_wrapper( files.upload, ) class AsyncFilesWithStreamingResponse: def __init__(self, files: AsyncFiles) -> None: self._files = files self.list = async_to_streamed_response_wrapper( files.list, ) self.delete = async_to_streamed_response_wrapper( files.delete, ) self.download = async_to_custom_streamed_response_wrapper( files.download, AsyncStreamedBinaryAPIResponse, ) self.retrieve_metadata = async_to_streamed_response_wrapper( files.retrieve_metadata, ) self.upload = async_to_streamed_response_wrapper( files.upload, ) anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/memory_stores/000077500000000000000000000000001523216435200263565ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/memory_stores/__init__.py000066400000000000000000000025361523216435200304750ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .memories import ( Memories, AsyncMemories, MemoriesWithRawResponse, AsyncMemoriesWithRawResponse, MemoriesWithStreamingResponse, AsyncMemoriesWithStreamingResponse, ) from .memory_stores import ( MemoryStores, AsyncMemoryStores, MemoryStoresWithRawResponse, AsyncMemoryStoresWithRawResponse, MemoryStoresWithStreamingResponse, AsyncMemoryStoresWithStreamingResponse, ) from .memory_versions import ( MemoryVersions, AsyncMemoryVersions, MemoryVersionsWithRawResponse, AsyncMemoryVersionsWithRawResponse, MemoryVersionsWithStreamingResponse, AsyncMemoryVersionsWithStreamingResponse, ) __all__ = [ "Memories", "AsyncMemories", "MemoriesWithRawResponse", "AsyncMemoriesWithRawResponse", "MemoriesWithStreamingResponse", "AsyncMemoriesWithStreamingResponse", "MemoryVersions", "AsyncMemoryVersions", "MemoryVersionsWithRawResponse", "AsyncMemoryVersionsWithRawResponse", "MemoryVersionsWithStreamingResponse", "AsyncMemoryVersionsWithStreamingResponse", "MemoryStores", "AsyncMemoryStores", "MemoryStoresWithRawResponse", "AsyncMemoryStoresWithRawResponse", "MemoryStoresWithStreamingResponse", "AsyncMemoryStoresWithStreamingResponse", ] anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/memory_stores/memories.py000066400000000000000000001120211523216435200305450ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Any, List, Optional, cast from itertools import chain import httpx from .... import _legacy_response from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ...._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ....pagination import SyncPageCursor, AsyncPageCursor from ...._base_client import AsyncPaginator, make_request_options from ....types.beta.memory_stores import ( BetaManagedAgentsMemoryView, memory_list_params, memory_create_params, memory_delete_params, memory_update_params, memory_retrieve_params, ) from ....types.anthropic_beta_param import AnthropicBetaParam from ....types.beta.memory_stores.beta_managed_agents_memory import BetaManagedAgentsMemory from ....types.beta.memory_stores.beta_managed_agents_memory_view import BetaManagedAgentsMemoryView from ....types.beta.memory_stores.beta_managed_agents_deleted_memory import BetaManagedAgentsDeletedMemory from ....types.beta.memory_stores.beta_managed_agents_memory_list_item import BetaManagedAgentsMemoryListItem from ....types.beta.memory_stores.beta_managed_agents_precondition_param import BetaManagedAgentsPreconditionParam __all__ = ["Memories", "AsyncMemories"] class Memories(SyncAPIResource): @cached_property def with_raw_response(self) -> MemoriesWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return MemoriesWithRawResponse(self) @cached_property def with_streaming_response(self) -> MemoriesWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return MemoriesWithStreamingResponse(self) def create( self, memory_store_id: str, *, content: Optional[str], path: str, view: BetaManagedAgentsMemoryView | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsMemory: """Create a memory Args: content: UTF-8 text content for the new memory. Maximum 100 kB (102,400 bytes). Required; pass `""` explicitly to create an empty memory. path: Hierarchical path for the new memory, e.g. `/projects/foo/notes.md`. Must start with `/`, contain at least one non-empty segment, and be at most 1,024 bytes. Must not contain empty segments, `.` or `..` segments, control or format characters, and must be NFC-normalized. Paths are case-sensitive. view: Query parameter for view betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not memory_store_id: raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["agent-memory-2026-07-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "agent-memory-2026-07-22", **(extra_headers or {})} return self._post( path_template("/v1/memory_stores/{memory_store_id}/memories?beta=true", memory_store_id=memory_store_id), body=maybe_transform( { "content": content, "path": path, }, memory_create_params.MemoryCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform({"view": view}, memory_create_params.MemoryCreateParams), ), cast_to=BetaManagedAgentsMemory, ) def retrieve( self, memory_id: str, *, memory_store_id: str, view: BetaManagedAgentsMemoryView | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsMemory: """ Retrieve a memory Args: view: Query parameter for view betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not memory_store_id: raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") if not memory_id: raise ValueError(f"Expected a non-empty value for `memory_id` but received {memory_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["agent-memory-2026-07-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "agent-memory-2026-07-22", **(extra_headers or {})} return self._get( path_template( "/v1/memory_stores/{memory_store_id}/memories/{memory_id}?beta=true", memory_store_id=memory_store_id, memory_id=memory_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform({"view": view}, memory_retrieve_params.MemoryRetrieveParams), ), cast_to=BetaManagedAgentsMemory, ) def update( self, memory_id: str, *, memory_store_id: str, view: BetaManagedAgentsMemoryView | Omit = omit, content: Optional[str] | Omit = omit, path: Optional[str] | Omit = omit, precondition: BetaManagedAgentsPreconditionParam | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsMemory: """ Update a memory Args: view: Query parameter for view content: New UTF-8 text content for the memory. Maximum 100 kB (102,400 bytes). Omit to leave the content unchanged (e.g., for a rename-only update). path: New path for the memory (a rename). Must start with `/`, contain at least one non-empty segment, and be at most 1,024 bytes. Must not contain empty segments, `.` or `..` segments, control or format characters, and must be NFC-normalized. Paths are case-sensitive. The memory's `id` is preserved across renames. Omit to leave the path unchanged. precondition: Optimistic-concurrency precondition: the update applies only if the memory's stored `content_sha256` equals the supplied value. On mismatch, the request returns `memory_precondition_failed_error` (HTTP 409); re-read the memory and retry against the fresh state. If the precondition fails but the stored state already exactly matches the requested `content` and `path`, the server returns 200 instead of 409. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not memory_store_id: raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") if not memory_id: raise ValueError(f"Expected a non-empty value for `memory_id` but received {memory_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["agent-memory-2026-07-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "agent-memory-2026-07-22", **(extra_headers or {})} return self._post( path_template( "/v1/memory_stores/{memory_store_id}/memories/{memory_id}?beta=true", memory_store_id=memory_store_id, memory_id=memory_id, ), body=maybe_transform( { "content": content, "path": path, "precondition": precondition, }, memory_update_params.MemoryUpdateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform({"view": view}, memory_update_params.MemoryUpdateParams), ), cast_to=BetaManagedAgentsMemory, ) def list( self, memory_store_id: str, *, depth: int | Omit = omit, limit: int | Omit = omit, page: str | Omit = omit, path_prefix: str | Omit = omit, view: BetaManagedAgentsMemoryView | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncPageCursor[BetaManagedAgentsMemoryListItem]: """ List memories Args: depth: `0` (or omitted) returns all descendants below `path_prefix` (recursive). `1` returns immediate children only; deeper entries roll up as `memory_prefix` items. `depth=1` behaves like `ls`; omitting `depth` behaves like `find`. limit: Maximum number of items to return per page. Must be between 1 and 100. Defaults to 20 when omitted. Capped at 20 when `view=full`. Both `memory` and `memory_prefix` items count toward the limit. page: Opaque pagination cursor (a `page_...` value). Pass the `next_page` value from a previous response to fetch the next page; omit for the first page. path_prefix: Optional path prefix filter. Must end with `/` (segment-aligned), e.g., `/notes/`. This value appears in request URLs. Do not include secrets or personally identifiable information. view: Which projection of each `memory` to return. Defaults to `basic` (content omitted). `full` populates `content` on each item and caps `limit` at 20; use this as the bulk-read path for export and sync. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not memory_store_id: raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["agent-memory-2026-07-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "agent-memory-2026-07-22", **(extra_headers or {})} return self._get_api_list( path_template("/v1/memory_stores/{memory_store_id}/memories?beta=true", memory_store_id=memory_store_id), page=SyncPageCursor[BetaManagedAgentsMemoryListItem], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "depth": depth, "limit": limit, "page": page, "path_prefix": path_prefix, "view": view, }, memory_list_params.MemoryListParams, ), ), model=cast( Any, BetaManagedAgentsMemoryListItem ), # Union types cannot be passed in as arguments in the type system ) def delete( self, memory_id: str, *, memory_store_id: str, expected_content_sha256: str | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsDeletedMemory: """ Delete a memory Args: expected_content_sha256: Query parameter for expected_content_sha256 betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not memory_store_id: raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") if not memory_id: raise ValueError(f"Expected a non-empty value for `memory_id` but received {memory_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["agent-memory-2026-07-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "agent-memory-2026-07-22", **(extra_headers or {})} return self._delete( path_template( "/v1/memory_stores/{memory_store_id}/memories/{memory_id}?beta=true", memory_store_id=memory_store_id, memory_id=memory_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( {"expected_content_sha256": expected_content_sha256}, memory_delete_params.MemoryDeleteParams ), ), cast_to=BetaManagedAgentsDeletedMemory, ) class AsyncMemories(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncMemoriesWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncMemoriesWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncMemoriesWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncMemoriesWithStreamingResponse(self) async def create( self, memory_store_id: str, *, content: Optional[str], path: str, view: BetaManagedAgentsMemoryView | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsMemory: """Create a memory Args: content: UTF-8 text content for the new memory. Maximum 100 kB (102,400 bytes). Required; pass `""` explicitly to create an empty memory. path: Hierarchical path for the new memory, e.g. `/projects/foo/notes.md`. Must start with `/`, contain at least one non-empty segment, and be at most 1,024 bytes. Must not contain empty segments, `.` or `..` segments, control or format characters, and must be NFC-normalized. Paths are case-sensitive. view: Query parameter for view betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not memory_store_id: raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["agent-memory-2026-07-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "agent-memory-2026-07-22", **(extra_headers or {})} return await self._post( path_template("/v1/memory_stores/{memory_store_id}/memories?beta=true", memory_store_id=memory_store_id), body=await async_maybe_transform( { "content": content, "path": path, }, memory_create_params.MemoryCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=await async_maybe_transform({"view": view}, memory_create_params.MemoryCreateParams), ), cast_to=BetaManagedAgentsMemory, ) async def retrieve( self, memory_id: str, *, memory_store_id: str, view: BetaManagedAgentsMemoryView | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsMemory: """ Retrieve a memory Args: view: Query parameter for view betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not memory_store_id: raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") if not memory_id: raise ValueError(f"Expected a non-empty value for `memory_id` but received {memory_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["agent-memory-2026-07-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "agent-memory-2026-07-22", **(extra_headers or {})} return await self._get( path_template( "/v1/memory_stores/{memory_store_id}/memories/{memory_id}?beta=true", memory_store_id=memory_store_id, memory_id=memory_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=await async_maybe_transform({"view": view}, memory_retrieve_params.MemoryRetrieveParams), ), cast_to=BetaManagedAgentsMemory, ) async def update( self, memory_id: str, *, memory_store_id: str, view: BetaManagedAgentsMemoryView | Omit = omit, content: Optional[str] | Omit = omit, path: Optional[str] | Omit = omit, precondition: BetaManagedAgentsPreconditionParam | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsMemory: """ Update a memory Args: view: Query parameter for view content: New UTF-8 text content for the memory. Maximum 100 kB (102,400 bytes). Omit to leave the content unchanged (e.g., for a rename-only update). path: New path for the memory (a rename). Must start with `/`, contain at least one non-empty segment, and be at most 1,024 bytes. Must not contain empty segments, `.` or `..` segments, control or format characters, and must be NFC-normalized. Paths are case-sensitive. The memory's `id` is preserved across renames. Omit to leave the path unchanged. precondition: Optimistic-concurrency precondition: the update applies only if the memory's stored `content_sha256` equals the supplied value. On mismatch, the request returns `memory_precondition_failed_error` (HTTP 409); re-read the memory and retry against the fresh state. If the precondition fails but the stored state already exactly matches the requested `content` and `path`, the server returns 200 instead of 409. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not memory_store_id: raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") if not memory_id: raise ValueError(f"Expected a non-empty value for `memory_id` but received {memory_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["agent-memory-2026-07-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "agent-memory-2026-07-22", **(extra_headers or {})} return await self._post( path_template( "/v1/memory_stores/{memory_store_id}/memories/{memory_id}?beta=true", memory_store_id=memory_store_id, memory_id=memory_id, ), body=await async_maybe_transform( { "content": content, "path": path, "precondition": precondition, }, memory_update_params.MemoryUpdateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=await async_maybe_transform({"view": view}, memory_update_params.MemoryUpdateParams), ), cast_to=BetaManagedAgentsMemory, ) def list( self, memory_store_id: str, *, depth: int | Omit = omit, limit: int | Omit = omit, page: str | Omit = omit, path_prefix: str | Omit = omit, view: BetaManagedAgentsMemoryView | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[BetaManagedAgentsMemoryListItem, AsyncPageCursor[BetaManagedAgentsMemoryListItem]]: """ List memories Args: depth: `0` (or omitted) returns all descendants below `path_prefix` (recursive). `1` returns immediate children only; deeper entries roll up as `memory_prefix` items. `depth=1` behaves like `ls`; omitting `depth` behaves like `find`. limit: Maximum number of items to return per page. Must be between 1 and 100. Defaults to 20 when omitted. Capped at 20 when `view=full`. Both `memory` and `memory_prefix` items count toward the limit. page: Opaque pagination cursor (a `page_...` value). Pass the `next_page` value from a previous response to fetch the next page; omit for the first page. path_prefix: Optional path prefix filter. Must end with `/` (segment-aligned), e.g., `/notes/`. This value appears in request URLs. Do not include secrets or personally identifiable information. view: Which projection of each `memory` to return. Defaults to `basic` (content omitted). `full` populates `content` on each item and caps `limit` at 20; use this as the bulk-read path for export and sync. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not memory_store_id: raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["agent-memory-2026-07-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "agent-memory-2026-07-22", **(extra_headers or {})} return self._get_api_list( path_template("/v1/memory_stores/{memory_store_id}/memories?beta=true", memory_store_id=memory_store_id), page=AsyncPageCursor[BetaManagedAgentsMemoryListItem], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "depth": depth, "limit": limit, "page": page, "path_prefix": path_prefix, "view": view, }, memory_list_params.MemoryListParams, ), ), model=cast( Any, BetaManagedAgentsMemoryListItem ), # Union types cannot be passed in as arguments in the type system ) async def delete( self, memory_id: str, *, memory_store_id: str, expected_content_sha256: str | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsDeletedMemory: """ Delete a memory Args: expected_content_sha256: Query parameter for expected_content_sha256 betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not memory_store_id: raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") if not memory_id: raise ValueError(f"Expected a non-empty value for `memory_id` but received {memory_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["agent-memory-2026-07-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "agent-memory-2026-07-22", **(extra_headers or {})} return await self._delete( path_template( "/v1/memory_stores/{memory_store_id}/memories/{memory_id}?beta=true", memory_store_id=memory_store_id, memory_id=memory_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=await async_maybe_transform( {"expected_content_sha256": expected_content_sha256}, memory_delete_params.MemoryDeleteParams ), ), cast_to=BetaManagedAgentsDeletedMemory, ) class MemoriesWithRawResponse: def __init__(self, memories: Memories) -> None: self._memories = memories self.create = _legacy_response.to_raw_response_wrapper( memories.create, ) self.retrieve = _legacy_response.to_raw_response_wrapper( memories.retrieve, ) self.update = _legacy_response.to_raw_response_wrapper( memories.update, ) self.list = _legacy_response.to_raw_response_wrapper( memories.list, ) self.delete = _legacy_response.to_raw_response_wrapper( memories.delete, ) class AsyncMemoriesWithRawResponse: def __init__(self, memories: AsyncMemories) -> None: self._memories = memories self.create = _legacy_response.async_to_raw_response_wrapper( memories.create, ) self.retrieve = _legacy_response.async_to_raw_response_wrapper( memories.retrieve, ) self.update = _legacy_response.async_to_raw_response_wrapper( memories.update, ) self.list = _legacy_response.async_to_raw_response_wrapper( memories.list, ) self.delete = _legacy_response.async_to_raw_response_wrapper( memories.delete, ) class MemoriesWithStreamingResponse: def __init__(self, memories: Memories) -> None: self._memories = memories self.create = to_streamed_response_wrapper( memories.create, ) self.retrieve = to_streamed_response_wrapper( memories.retrieve, ) self.update = to_streamed_response_wrapper( memories.update, ) self.list = to_streamed_response_wrapper( memories.list, ) self.delete = to_streamed_response_wrapper( memories.delete, ) class AsyncMemoriesWithStreamingResponse: def __init__(self, memories: AsyncMemories) -> None: self._memories = memories self.create = async_to_streamed_response_wrapper( memories.create, ) self.retrieve = async_to_streamed_response_wrapper( memories.retrieve, ) self.update = async_to_streamed_response_wrapper( memories.update, ) self.list = async_to_streamed_response_wrapper( memories.list, ) self.delete = async_to_streamed_response_wrapper( memories.delete, ) anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/memory_stores/memory_stores.py000066400000000000000000001133731523216435200316470ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Union, Optional from datetime import datetime from itertools import chain import httpx from .... import _legacy_response from .memories import ( Memories, AsyncMemories, MemoriesWithRawResponse, AsyncMemoriesWithRawResponse, MemoriesWithStreamingResponse, AsyncMemoriesWithStreamingResponse, ) from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ...._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ....pagination import SyncPageCursor, AsyncPageCursor from ....types.beta import memory_store_list_params, memory_store_create_params, memory_store_update_params from ...._base_client import AsyncPaginator, make_request_options from .memory_versions import ( MemoryVersions, AsyncMemoryVersions, MemoryVersionsWithRawResponse, AsyncMemoryVersionsWithRawResponse, MemoryVersionsWithStreamingResponse, AsyncMemoryVersionsWithStreamingResponse, ) from ....types.anthropic_beta_param import AnthropicBetaParam from ....types.beta.beta_managed_agents_memory_store import BetaManagedAgentsMemoryStore from ....types.beta.beta_managed_agents_deleted_memory_store import BetaManagedAgentsDeletedMemoryStore __all__ = ["MemoryStores", "AsyncMemoryStores"] class MemoryStores(SyncAPIResource): @cached_property def memories(self) -> Memories: return Memories(self._client) @cached_property def memory_versions(self) -> MemoryVersions: return MemoryVersions(self._client) @cached_property def with_raw_response(self) -> MemoryStoresWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return MemoryStoresWithRawResponse(self) @cached_property def with_streaming_response(self) -> MemoryStoresWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return MemoryStoresWithStreamingResponse(self) def create( self, *, name: str, description: str | Omit = omit, metadata: Dict[str, str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsMemoryStore: """Create a memory store Args: name: Human-readable name for the store. Required; 1–255 characters; no control characters. The mount-path slug under `/mnt/memory/` is derived from this name (lowercased, non-alphanumeric runs collapsed to a hyphen). Names need not be unique within a workspace. description: Free-text description of what the store contains, up to 1024 characters. Included in the agent's system prompt when the store is attached, so word it to be useful to the agent. metadata: Arbitrary key-value tags for your own bookkeeping (such as the end user a store belongs to). Up to 16 pairs; keys 1–64 characters; values up to 512 characters. Not visible to the agent. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["agent-memory-2026-07-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "agent-memory-2026-07-22", **(extra_headers or {})} return self._post( "/v1/memory_stores?beta=true", body=maybe_transform( { "name": name, "description": description, "metadata": metadata, }, memory_store_create_params.MemoryStoreCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsMemoryStore, ) def retrieve( self, memory_store_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsMemoryStore: """ Retrieve a memory store Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not memory_store_id: raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["agent-memory-2026-07-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "agent-memory-2026-07-22", **(extra_headers or {})} return self._get( path_template("/v1/memory_stores/{memory_store_id}?beta=true", memory_store_id=memory_store_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsMemoryStore, ) def update( self, memory_store_id: str, *, description: Optional[str] | Omit = omit, metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, name: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsMemoryStore: """ Update a memory store Args: description: New description for the store, up to 1024 characters. Pass an empty string to clear it. metadata: Metadata patch. Set a key to a string to upsert it, or to null to delete it. Omit the field to preserve. The stored bag is limited to 16 keys (up to 64 chars each) with values up to 512 chars. name: New human-readable name for the store. 1–255 characters; no control characters. Renaming changes the slug used for the store's `mount_path` in sessions created after the update. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not memory_store_id: raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["agent-memory-2026-07-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "agent-memory-2026-07-22", **(extra_headers or {})} return self._post( path_template("/v1/memory_stores/{memory_store_id}?beta=true", memory_store_id=memory_store_id), body=maybe_transform( { "description": description, "metadata": metadata, "name": name, }, memory_store_update_params.MemoryStoreUpdateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsMemoryStore, ) def list( self, *, created_at_gte: Union[str, datetime] | Omit = omit, created_at_lte: Union[str, datetime] | Omit = omit, include_archived: bool | Omit = omit, limit: int | Omit = omit, page: str | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncPageCursor[BetaManagedAgentsMemoryStore]: """ List memory stores Args: created_at_gte: Return only stores whose `created_at` is at or after this time (inclusive). Sent on the wire as `created_at[gte]`. created_at_lte: Return only stores whose `created_at` is at or before this time (inclusive). Sent on the wire as `created_at[lte]`. include_archived: When `true`, archived stores are included in the results. Defaults to `false` (archived stores are excluded). limit: Maximum number of stores to return per page. Must be between 1 and 100. Defaults to 20 when omitted. page: Opaque pagination cursor (a `page_...` value). Pass the `next_page` value from a previous response to fetch the next page; omit for the first page. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["agent-memory-2026-07-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "agent-memory-2026-07-22", **(extra_headers or {})} return self._get_api_list( "/v1/memory_stores?beta=true", page=SyncPageCursor[BetaManagedAgentsMemoryStore], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "created_at_gte": created_at_gte, "created_at_lte": created_at_lte, "include_archived": include_archived, "limit": limit, "page": page, }, memory_store_list_params.MemoryStoreListParams, ), ), model=BetaManagedAgentsMemoryStore, ) def delete( self, memory_store_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsDeletedMemoryStore: """ Delete a memory store Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not memory_store_id: raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["agent-memory-2026-07-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "agent-memory-2026-07-22", **(extra_headers or {})} return self._delete( path_template("/v1/memory_stores/{memory_store_id}?beta=true", memory_store_id=memory_store_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsDeletedMemoryStore, ) def archive( self, memory_store_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsMemoryStore: """ Archive a memory store Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not memory_store_id: raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["agent-memory-2026-07-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "agent-memory-2026-07-22", **(extra_headers or {})} return self._post( path_template("/v1/memory_stores/{memory_store_id}/archive?beta=true", memory_store_id=memory_store_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsMemoryStore, ) class AsyncMemoryStores(AsyncAPIResource): @cached_property def memories(self) -> AsyncMemories: return AsyncMemories(self._client) @cached_property def memory_versions(self) -> AsyncMemoryVersions: return AsyncMemoryVersions(self._client) @cached_property def with_raw_response(self) -> AsyncMemoryStoresWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncMemoryStoresWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncMemoryStoresWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncMemoryStoresWithStreamingResponse(self) async def create( self, *, name: str, description: str | Omit = omit, metadata: Dict[str, str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsMemoryStore: """Create a memory store Args: name: Human-readable name for the store. Required; 1–255 characters; no control characters. The mount-path slug under `/mnt/memory/` is derived from this name (lowercased, non-alphanumeric runs collapsed to a hyphen). Names need not be unique within a workspace. description: Free-text description of what the store contains, up to 1024 characters. Included in the agent's system prompt when the store is attached, so word it to be useful to the agent. metadata: Arbitrary key-value tags for your own bookkeeping (such as the end user a store belongs to). Up to 16 pairs; keys 1–64 characters; values up to 512 characters. Not visible to the agent. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["agent-memory-2026-07-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "agent-memory-2026-07-22", **(extra_headers or {})} return await self._post( "/v1/memory_stores?beta=true", body=await async_maybe_transform( { "name": name, "description": description, "metadata": metadata, }, memory_store_create_params.MemoryStoreCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsMemoryStore, ) async def retrieve( self, memory_store_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsMemoryStore: """ Retrieve a memory store Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not memory_store_id: raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["agent-memory-2026-07-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "agent-memory-2026-07-22", **(extra_headers or {})} return await self._get( path_template("/v1/memory_stores/{memory_store_id}?beta=true", memory_store_id=memory_store_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsMemoryStore, ) async def update( self, memory_store_id: str, *, description: Optional[str] | Omit = omit, metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, name: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsMemoryStore: """ Update a memory store Args: description: New description for the store, up to 1024 characters. Pass an empty string to clear it. metadata: Metadata patch. Set a key to a string to upsert it, or to null to delete it. Omit the field to preserve. The stored bag is limited to 16 keys (up to 64 chars each) with values up to 512 chars. name: New human-readable name for the store. 1–255 characters; no control characters. Renaming changes the slug used for the store's `mount_path` in sessions created after the update. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not memory_store_id: raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["agent-memory-2026-07-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "agent-memory-2026-07-22", **(extra_headers or {})} return await self._post( path_template("/v1/memory_stores/{memory_store_id}?beta=true", memory_store_id=memory_store_id), body=await async_maybe_transform( { "description": description, "metadata": metadata, "name": name, }, memory_store_update_params.MemoryStoreUpdateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsMemoryStore, ) def list( self, *, created_at_gte: Union[str, datetime] | Omit = omit, created_at_lte: Union[str, datetime] | Omit = omit, include_archived: bool | Omit = omit, limit: int | Omit = omit, page: str | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[BetaManagedAgentsMemoryStore, AsyncPageCursor[BetaManagedAgentsMemoryStore]]: """ List memory stores Args: created_at_gte: Return only stores whose `created_at` is at or after this time (inclusive). Sent on the wire as `created_at[gte]`. created_at_lte: Return only stores whose `created_at` is at or before this time (inclusive). Sent on the wire as `created_at[lte]`. include_archived: When `true`, archived stores are included in the results. Defaults to `false` (archived stores are excluded). limit: Maximum number of stores to return per page. Must be between 1 and 100. Defaults to 20 when omitted. page: Opaque pagination cursor (a `page_...` value). Pass the `next_page` value from a previous response to fetch the next page; omit for the first page. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["agent-memory-2026-07-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "agent-memory-2026-07-22", **(extra_headers or {})} return self._get_api_list( "/v1/memory_stores?beta=true", page=AsyncPageCursor[BetaManagedAgentsMemoryStore], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "created_at_gte": created_at_gte, "created_at_lte": created_at_lte, "include_archived": include_archived, "limit": limit, "page": page, }, memory_store_list_params.MemoryStoreListParams, ), ), model=BetaManagedAgentsMemoryStore, ) async def delete( self, memory_store_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsDeletedMemoryStore: """ Delete a memory store Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not memory_store_id: raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["agent-memory-2026-07-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "agent-memory-2026-07-22", **(extra_headers or {})} return await self._delete( path_template("/v1/memory_stores/{memory_store_id}?beta=true", memory_store_id=memory_store_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsDeletedMemoryStore, ) async def archive( self, memory_store_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsMemoryStore: """ Archive a memory store Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not memory_store_id: raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["agent-memory-2026-07-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "agent-memory-2026-07-22", **(extra_headers or {})} return await self._post( path_template("/v1/memory_stores/{memory_store_id}/archive?beta=true", memory_store_id=memory_store_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsMemoryStore, ) class MemoryStoresWithRawResponse: def __init__(self, memory_stores: MemoryStores) -> None: self._memory_stores = memory_stores self.create = _legacy_response.to_raw_response_wrapper( memory_stores.create, ) self.retrieve = _legacy_response.to_raw_response_wrapper( memory_stores.retrieve, ) self.update = _legacy_response.to_raw_response_wrapper( memory_stores.update, ) self.list = _legacy_response.to_raw_response_wrapper( memory_stores.list, ) self.delete = _legacy_response.to_raw_response_wrapper( memory_stores.delete, ) self.archive = _legacy_response.to_raw_response_wrapper( memory_stores.archive, ) @cached_property def memories(self) -> MemoriesWithRawResponse: return MemoriesWithRawResponse(self._memory_stores.memories) @cached_property def memory_versions(self) -> MemoryVersionsWithRawResponse: return MemoryVersionsWithRawResponse(self._memory_stores.memory_versions) class AsyncMemoryStoresWithRawResponse: def __init__(self, memory_stores: AsyncMemoryStores) -> None: self._memory_stores = memory_stores self.create = _legacy_response.async_to_raw_response_wrapper( memory_stores.create, ) self.retrieve = _legacy_response.async_to_raw_response_wrapper( memory_stores.retrieve, ) self.update = _legacy_response.async_to_raw_response_wrapper( memory_stores.update, ) self.list = _legacy_response.async_to_raw_response_wrapper( memory_stores.list, ) self.delete = _legacy_response.async_to_raw_response_wrapper( memory_stores.delete, ) self.archive = _legacy_response.async_to_raw_response_wrapper( memory_stores.archive, ) @cached_property def memories(self) -> AsyncMemoriesWithRawResponse: return AsyncMemoriesWithRawResponse(self._memory_stores.memories) @cached_property def memory_versions(self) -> AsyncMemoryVersionsWithRawResponse: return AsyncMemoryVersionsWithRawResponse(self._memory_stores.memory_versions) class MemoryStoresWithStreamingResponse: def __init__(self, memory_stores: MemoryStores) -> None: self._memory_stores = memory_stores self.create = to_streamed_response_wrapper( memory_stores.create, ) self.retrieve = to_streamed_response_wrapper( memory_stores.retrieve, ) self.update = to_streamed_response_wrapper( memory_stores.update, ) self.list = to_streamed_response_wrapper( memory_stores.list, ) self.delete = to_streamed_response_wrapper( memory_stores.delete, ) self.archive = to_streamed_response_wrapper( memory_stores.archive, ) @cached_property def memories(self) -> MemoriesWithStreamingResponse: return MemoriesWithStreamingResponse(self._memory_stores.memories) @cached_property def memory_versions(self) -> MemoryVersionsWithStreamingResponse: return MemoryVersionsWithStreamingResponse(self._memory_stores.memory_versions) class AsyncMemoryStoresWithStreamingResponse: def __init__(self, memory_stores: AsyncMemoryStores) -> None: self._memory_stores = memory_stores self.create = async_to_streamed_response_wrapper( memory_stores.create, ) self.retrieve = async_to_streamed_response_wrapper( memory_stores.retrieve, ) self.update = async_to_streamed_response_wrapper( memory_stores.update, ) self.list = async_to_streamed_response_wrapper( memory_stores.list, ) self.delete = async_to_streamed_response_wrapper( memory_stores.delete, ) self.archive = async_to_streamed_response_wrapper( memory_stores.archive, ) @cached_property def memories(self) -> AsyncMemoriesWithStreamingResponse: return AsyncMemoriesWithStreamingResponse(self._memory_stores.memories) @cached_property def memory_versions(self) -> AsyncMemoryVersionsWithStreamingResponse: return AsyncMemoryVersionsWithStreamingResponse(self._memory_stores.memory_versions) anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/memory_stores/memory_versions.py000066400000000000000000000535171523216435200322030ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Union from datetime import datetime from itertools import chain import httpx from .... import _legacy_response from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ...._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ....pagination import SyncPageCursor, AsyncPageCursor from ...._base_client import AsyncPaginator, make_request_options from ....types.beta.memory_stores import ( BetaManagedAgentsMemoryView, BetaManagedAgentsMemoryVersionOperation, memory_version_list_params, memory_version_retrieve_params, ) from ....types.anthropic_beta_param import AnthropicBetaParam from ....types.beta.memory_stores.beta_managed_agents_memory_view import BetaManagedAgentsMemoryView from ....types.beta.memory_stores.beta_managed_agents_memory_version import BetaManagedAgentsMemoryVersion from ....types.beta.memory_stores.beta_managed_agents_memory_version_operation import ( BetaManagedAgentsMemoryVersionOperation, ) __all__ = ["MemoryVersions", "AsyncMemoryVersions"] class MemoryVersions(SyncAPIResource): @cached_property def with_raw_response(self) -> MemoryVersionsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return MemoryVersionsWithRawResponse(self) @cached_property def with_streaming_response(self) -> MemoryVersionsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return MemoryVersionsWithStreamingResponse(self) def retrieve( self, memory_version_id: str, *, memory_store_id: str, view: BetaManagedAgentsMemoryView | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsMemoryVersion: """ Retrieve a memory version Args: view: Query parameter for view betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not memory_store_id: raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") if not memory_version_id: raise ValueError(f"Expected a non-empty value for `memory_version_id` but received {memory_version_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["agent-memory-2026-07-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "agent-memory-2026-07-22", **(extra_headers or {})} return self._get( path_template( "/v1/memory_stores/{memory_store_id}/memory_versions/{memory_version_id}?beta=true", memory_store_id=memory_store_id, memory_version_id=memory_version_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform({"view": view}, memory_version_retrieve_params.MemoryVersionRetrieveParams), ), cast_to=BetaManagedAgentsMemoryVersion, ) def list( self, memory_store_id: str, *, api_key_id: str | Omit = omit, created_at_gte: Union[str, datetime] | Omit = omit, created_at_lte: Union[str, datetime] | Omit = omit, limit: int | Omit = omit, memory_id: str | Omit = omit, operation: BetaManagedAgentsMemoryVersionOperation | Omit = omit, page: str | Omit = omit, session_id: str | Omit = omit, view: BetaManagedAgentsMemoryView | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncPageCursor[BetaManagedAgentsMemoryVersion]: """ List memory versions Args: api_key_id: Query parameter for api_key_id created_at_gte: Return versions created at or after this time (inclusive). created_at_lte: Return versions created at or before this time (inclusive). limit: Query parameter for limit memory_id: Query parameter for memory_id operation: Query parameter for operation page: Query parameter for page session_id: Query parameter for session_id view: Query parameter for view betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not memory_store_id: raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["agent-memory-2026-07-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "agent-memory-2026-07-22", **(extra_headers or {})} return self._get_api_list( path_template( "/v1/memory_stores/{memory_store_id}/memory_versions?beta=true", memory_store_id=memory_store_id ), page=SyncPageCursor[BetaManagedAgentsMemoryVersion], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "api_key_id": api_key_id, "created_at_gte": created_at_gte, "created_at_lte": created_at_lte, "limit": limit, "memory_id": memory_id, "operation": operation, "page": page, "session_id": session_id, "view": view, }, memory_version_list_params.MemoryVersionListParams, ), ), model=BetaManagedAgentsMemoryVersion, ) def redact( self, memory_version_id: str, *, memory_store_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsMemoryVersion: """ Redact a memory version Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not memory_store_id: raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") if not memory_version_id: raise ValueError(f"Expected a non-empty value for `memory_version_id` but received {memory_version_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["agent-memory-2026-07-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "agent-memory-2026-07-22", **(extra_headers or {})} return self._post( path_template( "/v1/memory_stores/{memory_store_id}/memory_versions/{memory_version_id}/redact?beta=true", memory_store_id=memory_store_id, memory_version_id=memory_version_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsMemoryVersion, ) class AsyncMemoryVersions(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncMemoryVersionsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncMemoryVersionsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncMemoryVersionsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncMemoryVersionsWithStreamingResponse(self) async def retrieve( self, memory_version_id: str, *, memory_store_id: str, view: BetaManagedAgentsMemoryView | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsMemoryVersion: """ Retrieve a memory version Args: view: Query parameter for view betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not memory_store_id: raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") if not memory_version_id: raise ValueError(f"Expected a non-empty value for `memory_version_id` but received {memory_version_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["agent-memory-2026-07-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "agent-memory-2026-07-22", **(extra_headers or {})} return await self._get( path_template( "/v1/memory_stores/{memory_store_id}/memory_versions/{memory_version_id}?beta=true", memory_store_id=memory_store_id, memory_version_id=memory_version_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=await async_maybe_transform( {"view": view}, memory_version_retrieve_params.MemoryVersionRetrieveParams ), ), cast_to=BetaManagedAgentsMemoryVersion, ) def list( self, memory_store_id: str, *, api_key_id: str | Omit = omit, created_at_gte: Union[str, datetime] | Omit = omit, created_at_lte: Union[str, datetime] | Omit = omit, limit: int | Omit = omit, memory_id: str | Omit = omit, operation: BetaManagedAgentsMemoryVersionOperation | Omit = omit, page: str | Omit = omit, session_id: str | Omit = omit, view: BetaManagedAgentsMemoryView | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[BetaManagedAgentsMemoryVersion, AsyncPageCursor[BetaManagedAgentsMemoryVersion]]: """ List memory versions Args: api_key_id: Query parameter for api_key_id created_at_gte: Return versions created at or after this time (inclusive). created_at_lte: Return versions created at or before this time (inclusive). limit: Query parameter for limit memory_id: Query parameter for memory_id operation: Query parameter for operation page: Query parameter for page session_id: Query parameter for session_id view: Query parameter for view betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not memory_store_id: raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["agent-memory-2026-07-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "agent-memory-2026-07-22", **(extra_headers or {})} return self._get_api_list( path_template( "/v1/memory_stores/{memory_store_id}/memory_versions?beta=true", memory_store_id=memory_store_id ), page=AsyncPageCursor[BetaManagedAgentsMemoryVersion], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "api_key_id": api_key_id, "created_at_gte": created_at_gte, "created_at_lte": created_at_lte, "limit": limit, "memory_id": memory_id, "operation": operation, "page": page, "session_id": session_id, "view": view, }, memory_version_list_params.MemoryVersionListParams, ), ), model=BetaManagedAgentsMemoryVersion, ) async def redact( self, memory_version_id: str, *, memory_store_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsMemoryVersion: """ Redact a memory version Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not memory_store_id: raise ValueError(f"Expected a non-empty value for `memory_store_id` but received {memory_store_id!r}") if not memory_version_id: raise ValueError(f"Expected a non-empty value for `memory_version_id` but received {memory_version_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["agent-memory-2026-07-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "agent-memory-2026-07-22", **(extra_headers or {})} return await self._post( path_template( "/v1/memory_stores/{memory_store_id}/memory_versions/{memory_version_id}/redact?beta=true", memory_store_id=memory_store_id, memory_version_id=memory_version_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsMemoryVersion, ) class MemoryVersionsWithRawResponse: def __init__(self, memory_versions: MemoryVersions) -> None: self._memory_versions = memory_versions self.retrieve = _legacy_response.to_raw_response_wrapper( memory_versions.retrieve, ) self.list = _legacy_response.to_raw_response_wrapper( memory_versions.list, ) self.redact = _legacy_response.to_raw_response_wrapper( memory_versions.redact, ) class AsyncMemoryVersionsWithRawResponse: def __init__(self, memory_versions: AsyncMemoryVersions) -> None: self._memory_versions = memory_versions self.retrieve = _legacy_response.async_to_raw_response_wrapper( memory_versions.retrieve, ) self.list = _legacy_response.async_to_raw_response_wrapper( memory_versions.list, ) self.redact = _legacy_response.async_to_raw_response_wrapper( memory_versions.redact, ) class MemoryVersionsWithStreamingResponse: def __init__(self, memory_versions: MemoryVersions) -> None: self._memory_versions = memory_versions self.retrieve = to_streamed_response_wrapper( memory_versions.retrieve, ) self.list = to_streamed_response_wrapper( memory_versions.list, ) self.redact = to_streamed_response_wrapper( memory_versions.redact, ) class AsyncMemoryVersionsWithStreamingResponse: def __init__(self, memory_versions: AsyncMemoryVersions) -> None: self._memory_versions = memory_versions self.retrieve = async_to_streamed_response_wrapper( memory_versions.retrieve, ) self.list = async_to_streamed_response_wrapper( memory_versions.list, ) self.redact = async_to_streamed_response_wrapper( memory_versions.redact, ) anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/messages/000077500000000000000000000000001523216435200252565ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/messages/__init__.py000066400000000000000000000015211523216435200273660ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .batches import ( Batches, AsyncBatches, BatchesWithRawResponse, AsyncBatchesWithRawResponse, BatchesWithStreamingResponse, AsyncBatchesWithStreamingResponse, ) from .messages import ( Messages, AsyncMessages, MessagesWithRawResponse, AsyncMessagesWithRawResponse, MessagesWithStreamingResponse, AsyncMessagesWithStreamingResponse, ) __all__ = [ "Batches", "AsyncBatches", "BatchesWithRawResponse", "AsyncBatchesWithRawResponse", "BatchesWithStreamingResponse", "AsyncBatchesWithStreamingResponse", "Messages", "AsyncMessages", "MessagesWithRawResponse", "AsyncMessagesWithRawResponse", "MessagesWithStreamingResponse", "AsyncMessagesWithStreamingResponse", ] anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/messages/batches.py000066400000000000000000001121771523216435200272520ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Iterable from itertools import chain import httpx from .... import _legacy_response from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ...._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ....pagination import SyncPage, AsyncPage from ...._exceptions import AnthropicError from ...._base_client import AsyncPaginator, make_request_options from ...._decoders.jsonl import JSONLDecoder, AsyncJSONLDecoder from ....types.beta.messages import batch_list_params, batch_create_params from ....types.anthropic_beta_param import AnthropicBetaParam from ....types.beta.messages.beta_message_batch import BetaMessageBatch from ....types.beta.messages.beta_deleted_message_batch import BetaDeletedMessageBatch from ....types.beta.messages.beta_message_batch_individual_response import BetaMessageBatchIndividualResponse __all__ = ["Batches", "AsyncBatches"] class Batches(SyncAPIResource): @cached_property def with_raw_response(self) -> BatchesWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return BatchesWithRawResponse(self) @cached_property def with_streaming_response(self) -> BatchesWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return BatchesWithStreamingResponse(self) def create( self, *, requests: Iterable[batch_create_params.Request], betas: List[AnthropicBetaParam] | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaMessageBatch: """ Send a batch of Message creation requests. The Message Batches API can be used to process multiple Messages API requests at once. Once a Message Batch is created, it begins processing immediately. Batches can take up to 24 hours to complete. Learn more about the Message Batches API in our [user guide](https://platform.claude.com/docs/en/build-with-claude/batch-processing) Args: requests: List of requests for prompt completion. Each is an individual request to create a Message. betas: Optional header to specify the beta version(s) you want to use. user_profile_id: The user profile ID to attribute the requests in this batch to. Use when acting on behalf of a party other than your organization. Requires the `user-profiles` beta header. Applies to every request in the batch; an individual request whose `user_profile_id` body field conflicts with this header is errored. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["message-batches-2024-09-24"])) if is_given(betas) else not_given, "anthropic-user-profile-id": user_profile_id, } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "message-batches-2024-09-24", **(extra_headers or {})} return self._post( "/v1/messages/batches?beta=true", body=maybe_transform({"requests": requests}, batch_create_params.BatchCreateParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaMessageBatch, ) def retrieve( self, message_batch_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaMessageBatch: """This endpoint is idempotent and can be used to poll for Message Batch completion. To access the results of a Message Batch, make a request to the `results_url` field in the response. Learn more about the Message Batches API in our [user guide](https://platform.claude.com/docs/en/build-with-claude/batch-processing) Args: message_batch_id: ID of the Message Batch. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not message_batch_id: raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["message-batches-2024-09-24"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "message-batches-2024-09-24", **(extra_headers or {})} return self._get( path_template("/v1/messages/batches/{message_batch_id}?beta=true", message_batch_id=message_batch_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaMessageBatch, ) def list( self, *, after_id: str | Omit = omit, before_id: str | Omit = omit, limit: int | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncPage[BetaMessageBatch]: """List all Message Batches within a Workspace. Most recently created batches are returned first. Learn more about the Message Batches API in our [user guide](https://platform.claude.com/docs/en/build-with-claude/batch-processing) Args: after_id: ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately after this object. before_id: ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately before this object. limit: Number of items to return per page. Defaults to `20`. Ranges from `1` to `1000`. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["message-batches-2024-09-24"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "message-batches-2024-09-24", **(extra_headers or {})} return self._get_api_list( "/v1/messages/batches?beta=true", page=SyncPage[BetaMessageBatch], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "after_id": after_id, "before_id": before_id, "limit": limit, }, batch_list_params.BatchListParams, ), ), model=BetaMessageBatch, ) def delete( self, message_batch_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaDeletedMessageBatch: """ Delete a Message Batch. Message Batches can only be deleted once they've finished processing. If you'd like to delete an in-progress batch, you must first cancel it. Learn more about the Message Batches API in our [user guide](https://platform.claude.com/docs/en/build-with-claude/batch-processing) Args: message_batch_id: ID of the Message Batch. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not message_batch_id: raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["message-batches-2024-09-24"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "message-batches-2024-09-24", **(extra_headers or {})} return self._delete( path_template("/v1/messages/batches/{message_batch_id}?beta=true", message_batch_id=message_batch_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaDeletedMessageBatch, ) def cancel( self, message_batch_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaMessageBatch: """Batches may be canceled any time before processing ends. Once cancellation is initiated, the batch enters a `canceling` state, at which time the system may complete any in-progress, non-interruptible requests before finalizing cancellation. The number of canceled requests is specified in `request_counts`. To determine which requests were canceled, check the individual results within the batch. Note that cancellation may not result in any canceled requests if they were non-interruptible. Learn more about the Message Batches API in our [user guide](https://platform.claude.com/docs/en/build-with-claude/batch-processing) Args: message_batch_id: ID of the Message Batch. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not message_batch_id: raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["message-batches-2024-09-24"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "message-batches-2024-09-24", **(extra_headers or {})} return self._post( path_template( "/v1/messages/batches/{message_batch_id}/cancel?beta=true", message_batch_id=message_batch_id ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaMessageBatch, ) def results( self, message_batch_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> JSONLDecoder[BetaMessageBatchIndividualResponse]: """ Streams the results of a Message Batch as a `.jsonl` file. Each line in the file is a JSON object containing the result of a single request in the Message Batch. Results are not guaranteed to be in the same order as requests. Use the `custom_id` field to match results to requests. Learn more about the Message Batches API in our [user guide](https://platform.claude.com/docs/en/build-with-claude/batch-processing) Args: message_batch_id: ID of the Message Batch. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not message_batch_id: raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") batch = self.retrieve(message_batch_id=message_batch_id) if not batch.results_url: raise AnthropicError( f"No `results_url` for the given batch; Has it finished processing? {batch.processing_status}" ) extra_headers = {"Accept": "application/binary", **(extra_headers or {})} extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["message-batches-2024-09-24"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "message-batches-2024-09-24", **(extra_headers or {})} return self._get( path_template(batch.results_url, message_batch_id=message_batch_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=JSONLDecoder[BetaMessageBatchIndividualResponse], stream=True, ) class AsyncBatches(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncBatchesWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncBatchesWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncBatchesWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncBatchesWithStreamingResponse(self) async def create( self, *, requests: Iterable[batch_create_params.Request], betas: List[AnthropicBetaParam] | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaMessageBatch: """ Send a batch of Message creation requests. The Message Batches API can be used to process multiple Messages API requests at once. Once a Message Batch is created, it begins processing immediately. Batches can take up to 24 hours to complete. Learn more about the Message Batches API in our [user guide](https://platform.claude.com/docs/en/build-with-claude/batch-processing) Args: requests: List of requests for prompt completion. Each is an individual request to create a Message. betas: Optional header to specify the beta version(s) you want to use. user_profile_id: The user profile ID to attribute the requests in this batch to. Use when acting on behalf of a party other than your organization. Requires the `user-profiles` beta header. Applies to every request in the batch; an individual request whose `user_profile_id` body field conflicts with this header is errored. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["message-batches-2024-09-24"])) if is_given(betas) else not_given, "anthropic-user-profile-id": user_profile_id, } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "message-batches-2024-09-24", **(extra_headers or {})} return await self._post( "/v1/messages/batches?beta=true", body=await async_maybe_transform({"requests": requests}, batch_create_params.BatchCreateParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaMessageBatch, ) async def retrieve( self, message_batch_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaMessageBatch: """This endpoint is idempotent and can be used to poll for Message Batch completion. To access the results of a Message Batch, make a request to the `results_url` field in the response. Learn more about the Message Batches API in our [user guide](https://platform.claude.com/docs/en/build-with-claude/batch-processing) Args: message_batch_id: ID of the Message Batch. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not message_batch_id: raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["message-batches-2024-09-24"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "message-batches-2024-09-24", **(extra_headers or {})} return await self._get( path_template("/v1/messages/batches/{message_batch_id}?beta=true", message_batch_id=message_batch_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaMessageBatch, ) def list( self, *, after_id: str | Omit = omit, before_id: str | Omit = omit, limit: int | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[BetaMessageBatch, AsyncPage[BetaMessageBatch]]: """List all Message Batches within a Workspace. Most recently created batches are returned first. Learn more about the Message Batches API in our [user guide](https://platform.claude.com/docs/en/build-with-claude/batch-processing) Args: after_id: ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately after this object. before_id: ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately before this object. limit: Number of items to return per page. Defaults to `20`. Ranges from `1` to `1000`. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["message-batches-2024-09-24"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "message-batches-2024-09-24", **(extra_headers or {})} return self._get_api_list( "/v1/messages/batches?beta=true", page=AsyncPage[BetaMessageBatch], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "after_id": after_id, "before_id": before_id, "limit": limit, }, batch_list_params.BatchListParams, ), ), model=BetaMessageBatch, ) async def delete( self, message_batch_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaDeletedMessageBatch: """ Delete a Message Batch. Message Batches can only be deleted once they've finished processing. If you'd like to delete an in-progress batch, you must first cancel it. Learn more about the Message Batches API in our [user guide](https://platform.claude.com/docs/en/build-with-claude/batch-processing) Args: message_batch_id: ID of the Message Batch. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not message_batch_id: raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["message-batches-2024-09-24"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "message-batches-2024-09-24", **(extra_headers or {})} return await self._delete( path_template("/v1/messages/batches/{message_batch_id}?beta=true", message_batch_id=message_batch_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaDeletedMessageBatch, ) async def cancel( self, message_batch_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaMessageBatch: """Batches may be canceled any time before processing ends. Once cancellation is initiated, the batch enters a `canceling` state, at which time the system may complete any in-progress, non-interruptible requests before finalizing cancellation. The number of canceled requests is specified in `request_counts`. To determine which requests were canceled, check the individual results within the batch. Note that cancellation may not result in any canceled requests if they were non-interruptible. Learn more about the Message Batches API in our [user guide](https://platform.claude.com/docs/en/build-with-claude/batch-processing) Args: message_batch_id: ID of the Message Batch. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not message_batch_id: raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["message-batches-2024-09-24"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "message-batches-2024-09-24", **(extra_headers or {})} return await self._post( path_template( "/v1/messages/batches/{message_batch_id}/cancel?beta=true", message_batch_id=message_batch_id ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaMessageBatch, ) async def results( self, message_batch_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncJSONLDecoder[BetaMessageBatchIndividualResponse]: """ Streams the results of a Message Batch as a `.jsonl` file. Each line in the file is a JSON object containing the result of a single request in the Message Batch. Results are not guaranteed to be in the same order as requests. Use the `custom_id` field to match results to requests. Learn more about the Message Batches API in our [user guide](https://platform.claude.com/docs/en/build-with-claude/batch-processing) Args: message_batch_id: ID of the Message Batch. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not message_batch_id: raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") batch = await self.retrieve(message_batch_id=message_batch_id) if not batch.results_url: raise AnthropicError( f"No `results_url` for the given batch; Has it finished processing? {batch.processing_status}" ) extra_headers = {"Accept": "application/binary", **(extra_headers or {})} extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["message-batches-2024-09-24"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "message-batches-2024-09-24", **(extra_headers or {})} return await self._get( path_template(batch.results_url, message_batch_id=message_batch_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=AsyncJSONLDecoder[BetaMessageBatchIndividualResponse], stream=True, ) class BatchesWithRawResponse: def __init__(self, batches: Batches) -> None: self._batches = batches self.create = _legacy_response.to_raw_response_wrapper( batches.create, ) self.retrieve = _legacy_response.to_raw_response_wrapper( batches.retrieve, ) self.list = _legacy_response.to_raw_response_wrapper( batches.list, ) self.delete = _legacy_response.to_raw_response_wrapper( batches.delete, ) self.cancel = _legacy_response.to_raw_response_wrapper( batches.cancel, ) self.results = _legacy_response.to_raw_response_wrapper( batches.results, ) class AsyncBatchesWithRawResponse: def __init__(self, batches: AsyncBatches) -> None: self._batches = batches self.create = _legacy_response.async_to_raw_response_wrapper( batches.create, ) self.retrieve = _legacy_response.async_to_raw_response_wrapper( batches.retrieve, ) self.list = _legacy_response.async_to_raw_response_wrapper( batches.list, ) self.delete = _legacy_response.async_to_raw_response_wrapper( batches.delete, ) self.cancel = _legacy_response.async_to_raw_response_wrapper( batches.cancel, ) self.results = _legacy_response.async_to_raw_response_wrapper( batches.results, ) class BatchesWithStreamingResponse: def __init__(self, batches: Batches) -> None: self._batches = batches self.create = to_streamed_response_wrapper( batches.create, ) self.retrieve = to_streamed_response_wrapper( batches.retrieve, ) self.list = to_streamed_response_wrapper( batches.list, ) self.delete = to_streamed_response_wrapper( batches.delete, ) self.cancel = to_streamed_response_wrapper( batches.cancel, ) self.results = to_streamed_response_wrapper( batches.results, ) class AsyncBatchesWithStreamingResponse: def __init__(self, batches: AsyncBatches) -> None: self._batches = batches self.create = async_to_streamed_response_wrapper( batches.create, ) self.retrieve = async_to_streamed_response_wrapper( batches.retrieve, ) self.list = async_to_streamed_response_wrapper( batches.list, ) self.delete = async_to_streamed_response_wrapper( batches.delete, ) self.cancel = async_to_streamed_response_wrapper( batches.cancel, ) self.results = async_to_streamed_response_wrapper( batches.results, ) anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/messages/messages.py000066400000000000000000006117061523216435200274520ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import inspect import warnings from typing import TYPE_CHECKING, List, Type, Union, Iterable, Optional, cast from functools import partial from itertools import chain from typing_extensions import Literal, overload import httpx import pydantic from .... import _legacy_response from .batches import ( Batches, AsyncBatches, BatchesWithRawResponse, AsyncBatchesWithRawResponse, BatchesWithStreamingResponse, AsyncBatchesWithStreamingResponse, ) from ...._types import NOT_GIVEN, Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given from ...._utils import is_given, required_args, maybe_transform, strip_not_given, async_maybe_transform from ...._compat import cached_property from ...._models import TypeAdapter from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ....lib.tools import ( BetaToolRunner, BetaAsyncToolRunner, BetaStreamingToolRunner, BetaAsyncStreamingToolRunner, ) from ...._constants import DEFAULT_TIMEOUT, MODEL_NONSTREAMING_TOKENS from ...._streaming import Stream, AsyncStream from ....types.beta import ( BetaFallbacksParam, BetaDiagnosticsParam, BetaThinkingConfigParam, message_create_params, message_count_tokens_params, ) from ...._exceptions import AnthropicError from ...._base_client import ( merge_headers, make_request_options, ) from ...._utils._utils import is_dict from ....lib.streaming import BetaMessageStreamManager, BetaAsyncMessageStreamManager from ...messages.messages import DEPRECATED_MODELS, MODELS_TO_WARN_WITH_THINKING_ENABLED from ....types.model_param import ModelParam from ....lib._parse._response import ResponseFormatT, parse_beta_response from ....lib._parse._transform import transform_schema from ....lib._stainless_helpers import ( HELPER_METHOD_STREAM as _HELPER_METHOD_STREAM, STAINLESS_HELPER_METHOD_HEADER as _STAINLESS_HELPER_METHOD_HEADER, STAINLESS_STREAM_HELPER_HEADER as _STAINLESS_STREAM_HELPER_HEADER, helper_header as _helper_header, stainless_helper_header as _stainless_helper_header, ) from ....types.beta.beta_message import BetaMessage from ....lib.tools._beta_functions import ( BetaFunctionTool, BetaRunnableTool, BetaAsyncFunctionTool, BetaAsyncRunnableTool, BetaBuiltinFunctionTool, BetaAsyncBuiltinFunctionTool, ) from ....types.anthropic_beta_param import AnthropicBetaParam from ....types.beta.beta_message_param import BetaMessageParam from ....types.beta.beta_metadata_param import BetaMetadataParam from ....types.beta.parsed_beta_message import ParsedBetaMessage from ....types.beta.beta_fallbacks_param import BetaFallbacksParam from ....types.beta.beta_text_block_param import BetaTextBlockParam from ....types.beta.beta_tool_union_param import BetaToolUnionParam from ....types.beta.beta_diagnostics_param import BetaDiagnosticsParam from ....types.beta.beta_tool_choice_param import BetaToolChoiceParam from ....lib.tools._beta_compaction_control import CompactionControl from ....types.beta.beta_output_config_param import BetaOutputConfigParam from ....types.beta.beta_message_tokens_count import BetaMessageTokensCount from ....types.beta.beta_thinking_config_param import BetaThinkingConfigParam from ....types.beta.beta_json_output_format_param import BetaJSONOutputFormatParam from ....types.beta.beta_raw_message_stream_event import BetaRawMessageStreamEvent from ....types.beta.beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam from ....types.beta.beta_context_management_config_param import BetaContextManagementConfigParam from ....types.beta.beta_request_mcp_server_url_definition_param import BetaRequestMCPServerURLDefinitionParam if TYPE_CHECKING: from ...._client import Anthropic, AsyncAnthropic __all__ = ["Messages", "AsyncMessages"] class Messages(SyncAPIResource): @cached_property def batches(self) -> Batches: return Batches(self._client) @cached_property def with_raw_response(self) -> MessagesWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return MessagesWithRawResponse(self) @cached_property def with_streaming_response(self) -> MessagesWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return MessagesWithStreamingResponse(self) @overload def create( self, *, max_tokens: int, messages: Iterable[BetaMessageParam], model: ModelParam, cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, container: Optional[message_create_params.Container] | Omit = omit, context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, diagnostics: Optional[BetaDiagnosticsParam] | Omit = omit, fallback_credit_token: Optional[message_create_params.FallbackCreditToken] | Omit = omit, fallbacks: Optional[BetaFallbacksParam] | Omit = omit, inference_geo: Optional[str] | Omit = omit, mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, metadata: BetaMetadataParam | Omit = omit, output_config: BetaOutputConfigParam | Omit = omit, output_format: Optional[BetaJSONOutputFormatParam] | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, speed: Optional[Literal["standard", "fast"]] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, stream: Literal[False] | Omit = omit, system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, temperature: float | Omit = omit, thinking: BetaThinkingConfigParam | Omit = omit, tool_choice: BetaToolChoiceParam | Omit = omit, tools: Iterable[BetaToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaMessage: """ Send a structured list of input messages with text and/or image content, and the model will generate the next message in the conversation. The Messages API can be used for either single queries or stateless multi-turn conversations. Learn more about the Messages API in our [user guide](https://platform.claude.com/docs/en/get-started) Args: max_tokens: The maximum number of tokens to generate before stopping. Note that our models may stop _before_ reaching this maximum. This parameter only specifies the absolute maximum number of tokens to generate. Set to `0` to populate the [prompt cache](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pre-warming-the-cache) without generating a response. Different models have different maximum values for this parameter. See [models](https://platform.claude.com/docs/en/about-claude/models/overview) for details. messages: Input messages. Our models are trained to operate on alternating `user` and `assistant` conversational turns. When creating a new `Message`, you specify the prior conversational turns with the `messages` parameter, and the model then generates the next `Message` in the conversation. Consecutive `user` or `assistant` turns in your request will be combined into a single turn. Each input message must be an object with a `role` and `content`. You can specify a single `user`-role message, or you can include multiple `user` and `assistant` messages. If the final message uses the `assistant` role, the response content will continue immediately from the content in that message. This can be used to constrain part of the model's response. Example with a single `user` message: ```json [{ "role": "user", "content": "Hello, Claude" }] ``` Example with multiple conversational turns: ```json [ { "role": "user", "content": "Hello there." }, { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, { "role": "user", "content": "Can you explain LLMs in plain English?" } ] ``` Example with a partially-filled response from Claude: ```json [ { "role": "user", "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" }, { "role": "assistant", "content": "The best answer is (" } ] ``` Each input message `content` may be either a single `string` or an array of content blocks, where each block has a specific `type`. Using a `string` for `content` is shorthand for an array of one content block of type `"text"`. The following input messages are equivalent: ```json { "role": "user", "content": "Hello, Claude" } ``` ```json { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } ``` See [input examples](https://platform.claude.com/docs/en/build-with-claude/working-with-messages). Note that if you want to include a [system prompt](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role), you can use the top-level `system` parameter — there is no `"system"` role for input messages in the Messages API. There is a limit of 100,000 messages in a single request. model: The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. cache_control: Top-level cache control automatically applies a cache_control marker to the last cacheable block in the request. container: Container identifier for reuse across requests. context_management: Context management configuration. This allows you to control how Claude manages context across multiple requests, such as whether to clear function results or not. diagnostics: Request-level diagnostics. Currently carries the previous response id for prompt-cache divergence reporting. fallback_credit_token: The `fallback_credit_token` from a prior refusal's `stop_details`. When a preceding request was refused and returned a `fallback_credit_token`, pass that code here on the retry to have the retry's cache-creation tokens for the prefix that was warm on the refused model billed at the cache-read rate. Must be redeemed by the same organization and workspace, with the same request body (optionally extended by one appended `assistant` message whose content is the partial text — with any trailing whitespace stripped from the final text block — and paired server-tool blocks streamed before the refusal; the appended-assistant form is not available for requests with `output_format` set or forced `tool_choice`), on an eligible fallback model, on the same platform, and within 5 minutes of the refusal; a mismatch is a 400. A token minted mid-server-tool-loop whose partial content was continuable may only be redeemed with the appended-assistant form — if an exact-body retry is rejected with a 400 saying the token must be redeemed by continuing the partial response, retry with the appended-assistant form instead. When the appended-assistant form is used on a model that otherwise disallows assistant-turn prefill, this token also authorizes that one prefill. fallbacks: Opt-in server-side retry on one or more substitute models when the requested model declines for policy reasons. Tried in order: if the first entry also declines, the second is tried, and so on. The string "default" requests the requested model's server-defined default fallback configuration. inference_geo: Specifies the geographic region for inference processing. If not specified, the workspace's `default_inference_geo` is used. mcp_servers: MCP servers to be utilized in this request metadata: An object describing metadata about the request. output_config: Configuration options for the model's output, such as the output format. output_format: Deprecated: Use `output_config.format` instead. See [structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs) A schema to specify Claude's output format in responses. This parameter will be removed in a future release. service_tier: Determines whether to use priority capacity (if available) or standard capacity for this request. Anthropic offers different levels of service for your API requests. See [service-tiers](https://platform.claude.com/docs/en/api/service-tiers) for details. speed: Inference speed mode. `fast` provides significantly faster output token generation at premium pricing. Not all models support `fast`; invalid combinations are rejected at create time. stop_sequences: Custom text sequences that will cause the model to stop generating. Our models will normally stop when they have naturally completed their turn, which will result in a response `stop_reason` of `"end_turn"`. If you want the model to stop generating when it encounters custom strings of text, you can use the `stop_sequences` parameter. If the model encounters one of the custom sequences, the response `stop_reason` value will be `"stop_sequence"` and the response `stop_sequence` value will contain the matched stop sequence. stream: Whether to incrementally stream the response using server-sent events. See [streaming](https://platform.claude.com/docs/en/build-with-claude/streaming) for details. system: System prompt. A system prompt is a way of providing context and instructions to Claude, such as specifying a particular goal or role. See our [guide to system prompts](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role). temperature: Amount of randomness injected into the response. Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` for analytical / multiple choice, and closer to `1.0` for creative and generative tasks. Note that even with `temperature` of `0.0`, the results will not be fully deterministic. thinking: Configuration for enabling Claude's extended thinking. When enabled, responses include `thinking` content blocks showing Claude's thinking process before the final answer. Requires a minimum budget of 1,024 tokens and counts towards your `max_tokens` limit. See [extended thinking](https://platform.claude.com/docs/en/build-with-claude/extended-thinking) for details. tool_choice: How the model should use the provided tools. The model can use a specific tool, any available tool, decide by itself, or not use tools at all. tools: Definitions of tools that the model may use. If you include `tools` in your API request, the model may return `tool_use` content blocks that represent the model's use of those tools. You can then run those tools using the tool input generated by the model and then optionally return results back to the model using `tool_result` content blocks. There are two types of tools: **client tools** and **server tools**. The behavior described below applies to client tools. For [server tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools), see their individual documentation as each has its own behavior (e.g., the [web search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool)). Each tool definition includes: - `name`: Name of the tool. - `description`: Optional, but strongly-recommended description of the tool. - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the tool `input` shape that the model will produce in `tool_use` output content blocks. For example, if you defined `tools` as: ```json [ { "name": "get_stock_price", "description": "Get the current stock price for a given ticker symbol.", "input_schema": { "type": "object", "properties": { "ticker": { "type": "string", "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." } }, "required": ["ticker"] } } ] ``` And then asked the model "What's the S&P 500 at today?", the model might produce `tool_use` content blocks in the response like this: ```json [ { "type": "tool_use", "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "name": "get_stock_price", "input": { "ticker": "^GSPC" } } ] ``` You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an input, and return the following back to the model in a subsequent `user` message: ```json [ { "type": "tool_result", "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "content": "259.75 USD" } ] ``` Tools can be used for workflows that include running client-side tools and functions, or more generally whenever you want the model to produce a particular JSON structure of output. See our [guide](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview) for more details. top_k: Only sample from the top K options for each subsequent token. Used to remove "long tail" low probability responses. [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). Recommended for advanced use cases only. top_p: Use nucleus sampling. In nucleus sampling, we compute the cumulative distribution over all the options for each subsequent token in decreasing probability order and cut it off once it reaches a particular probability specified by `top_p`. Recommended for advanced use cases only. betas: Optional header to specify the beta version(s) you want to use. user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a party other than your organization. Requires the `user-profiles` beta header. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ ... @overload def create( self, *, max_tokens: int, messages: Iterable[BetaMessageParam], model: ModelParam, stream: Literal[True], cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, container: Optional[message_create_params.Container] | Omit = omit, context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, diagnostics: Optional[BetaDiagnosticsParam] | Omit = omit, fallback_credit_token: Optional[message_create_params.FallbackCreditToken] | Omit = omit, fallbacks: Optional[BetaFallbacksParam] | Omit = omit, inference_geo: Optional[str] | Omit = omit, mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, metadata: BetaMetadataParam | Omit = omit, output_config: BetaOutputConfigParam | Omit = omit, output_format: Optional[BetaJSONOutputFormatParam] | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, speed: Optional[Literal["standard", "fast"]] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, temperature: float | Omit = omit, thinking: BetaThinkingConfigParam | Omit = omit, tool_choice: BetaToolChoiceParam | Omit = omit, tools: Iterable[BetaToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Stream[BetaRawMessageStreamEvent]: """ Send a structured list of input messages with text and/or image content, and the model will generate the next message in the conversation. The Messages API can be used for either single queries or stateless multi-turn conversations. Learn more about the Messages API in our [user guide](https://platform.claude.com/docs/en/get-started) Args: max_tokens: The maximum number of tokens to generate before stopping. Note that our models may stop _before_ reaching this maximum. This parameter only specifies the absolute maximum number of tokens to generate. Set to `0` to populate the [prompt cache](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pre-warming-the-cache) without generating a response. Different models have different maximum values for this parameter. See [models](https://platform.claude.com/docs/en/about-claude/models/overview) for details. messages: Input messages. Our models are trained to operate on alternating `user` and `assistant` conversational turns. When creating a new `Message`, you specify the prior conversational turns with the `messages` parameter, and the model then generates the next `Message` in the conversation. Consecutive `user` or `assistant` turns in your request will be combined into a single turn. Each input message must be an object with a `role` and `content`. You can specify a single `user`-role message, or you can include multiple `user` and `assistant` messages. If the final message uses the `assistant` role, the response content will continue immediately from the content in that message. This can be used to constrain part of the model's response. Example with a single `user` message: ```json [{ "role": "user", "content": "Hello, Claude" }] ``` Example with multiple conversational turns: ```json [ { "role": "user", "content": "Hello there." }, { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, { "role": "user", "content": "Can you explain LLMs in plain English?" } ] ``` Example with a partially-filled response from Claude: ```json [ { "role": "user", "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" }, { "role": "assistant", "content": "The best answer is (" } ] ``` Each input message `content` may be either a single `string` or an array of content blocks, where each block has a specific `type`. Using a `string` for `content` is shorthand for an array of one content block of type `"text"`. The following input messages are equivalent: ```json { "role": "user", "content": "Hello, Claude" } ``` ```json { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } ``` See [input examples](https://platform.claude.com/docs/en/build-with-claude/working-with-messages). Note that if you want to include a [system prompt](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role), you can use the top-level `system` parameter — there is no `"system"` role for input messages in the Messages API. There is a limit of 100,000 messages in a single request. model: The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. stream: Whether to incrementally stream the response using server-sent events. See [streaming](https://platform.claude.com/docs/en/build-with-claude/streaming) for details. cache_control: Top-level cache control automatically applies a cache_control marker to the last cacheable block in the request. container: Container identifier for reuse across requests. context_management: Context management configuration. This allows you to control how Claude manages context across multiple requests, such as whether to clear function results or not. diagnostics: Request-level diagnostics. Currently carries the previous response id for prompt-cache divergence reporting. fallback_credit_token: The `fallback_credit_token` from a prior refusal's `stop_details`. When a preceding request was refused and returned a `fallback_credit_token`, pass that code here on the retry to have the retry's cache-creation tokens for the prefix that was warm on the refused model billed at the cache-read rate. Must be redeemed by the same organization and workspace, with the same request body (optionally extended by one appended `assistant` message whose content is the partial text — with any trailing whitespace stripped from the final text block — and paired server-tool blocks streamed before the refusal; the appended-assistant form is not available for requests with `output_format` set or forced `tool_choice`), on an eligible fallback model, on the same platform, and within 5 minutes of the refusal; a mismatch is a 400. A token minted mid-server-tool-loop whose partial content was continuable may only be redeemed with the appended-assistant form — if an exact-body retry is rejected with a 400 saying the token must be redeemed by continuing the partial response, retry with the appended-assistant form instead. When the appended-assistant form is used on a model that otherwise disallows assistant-turn prefill, this token also authorizes that one prefill. fallbacks: Opt-in server-side retry on one or more substitute models when the requested model declines for policy reasons. Tried in order: if the first entry also declines, the second is tried, and so on. The string "default" requests the requested model's server-defined default fallback configuration. inference_geo: Specifies the geographic region for inference processing. If not specified, the workspace's `default_inference_geo` is used. mcp_servers: MCP servers to be utilized in this request metadata: An object describing metadata about the request. output_config: Configuration options for the model's output, such as the output format. output_format: Deprecated: Use `output_config.format` instead. See [structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs) A schema to specify Claude's output format in responses. This parameter will be removed in a future release. service_tier: Determines whether to use priority capacity (if available) or standard capacity for this request. Anthropic offers different levels of service for your API requests. See [service-tiers](https://platform.claude.com/docs/en/api/service-tiers) for details. speed: Inference speed mode. `fast` provides significantly faster output token generation at premium pricing. Not all models support `fast`; invalid combinations are rejected at create time. stop_sequences: Custom text sequences that will cause the model to stop generating. Our models will normally stop when they have naturally completed their turn, which will result in a response `stop_reason` of `"end_turn"`. If you want the model to stop generating when it encounters custom strings of text, you can use the `stop_sequences` parameter. If the model encounters one of the custom sequences, the response `stop_reason` value will be `"stop_sequence"` and the response `stop_sequence` value will contain the matched stop sequence. system: System prompt. A system prompt is a way of providing context and instructions to Claude, such as specifying a particular goal or role. See our [guide to system prompts](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role). temperature: Amount of randomness injected into the response. Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` for analytical / multiple choice, and closer to `1.0` for creative and generative tasks. Note that even with `temperature` of `0.0`, the results will not be fully deterministic. thinking: Configuration for enabling Claude's extended thinking. When enabled, responses include `thinking` content blocks showing Claude's thinking process before the final answer. Requires a minimum budget of 1,024 tokens and counts towards your `max_tokens` limit. See [extended thinking](https://platform.claude.com/docs/en/build-with-claude/extended-thinking) for details. tool_choice: How the model should use the provided tools. The model can use a specific tool, any available tool, decide by itself, or not use tools at all. tools: Definitions of tools that the model may use. If you include `tools` in your API request, the model may return `tool_use` content blocks that represent the model's use of those tools. You can then run those tools using the tool input generated by the model and then optionally return results back to the model using `tool_result` content blocks. There are two types of tools: **client tools** and **server tools**. The behavior described below applies to client tools. For [server tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools), see their individual documentation as each has its own behavior (e.g., the [web search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool)). Each tool definition includes: - `name`: Name of the tool. - `description`: Optional, but strongly-recommended description of the tool. - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the tool `input` shape that the model will produce in `tool_use` output content blocks. For example, if you defined `tools` as: ```json [ { "name": "get_stock_price", "description": "Get the current stock price for a given ticker symbol.", "input_schema": { "type": "object", "properties": { "ticker": { "type": "string", "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." } }, "required": ["ticker"] } } ] ``` And then asked the model "What's the S&P 500 at today?", the model might produce `tool_use` content blocks in the response like this: ```json [ { "type": "tool_use", "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "name": "get_stock_price", "input": { "ticker": "^GSPC" } } ] ``` You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an input, and return the following back to the model in a subsequent `user` message: ```json [ { "type": "tool_result", "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "content": "259.75 USD" } ] ``` Tools can be used for workflows that include running client-side tools and functions, or more generally whenever you want the model to produce a particular JSON structure of output. See our [guide](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview) for more details. top_k: Only sample from the top K options for each subsequent token. Used to remove "long tail" low probability responses. [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). Recommended for advanced use cases only. top_p: Use nucleus sampling. In nucleus sampling, we compute the cumulative distribution over all the options for each subsequent token in decreasing probability order and cut it off once it reaches a particular probability specified by `top_p`. Recommended for advanced use cases only. betas: Optional header to specify the beta version(s) you want to use. user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a party other than your organization. Requires the `user-profiles` beta header. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ ... @overload def create( self, *, max_tokens: int, messages: Iterable[BetaMessageParam], model: ModelParam, stream: bool, cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, container: Optional[message_create_params.Container] | Omit = omit, context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, diagnostics: Optional[BetaDiagnosticsParam] | Omit = omit, fallback_credit_token: Optional[message_create_params.FallbackCreditToken] | Omit = omit, fallbacks: Optional[BetaFallbacksParam] | Omit = omit, inference_geo: Optional[str] | Omit = omit, mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, metadata: BetaMetadataParam | Omit = omit, output_config: BetaOutputConfigParam | Omit = omit, output_format: Optional[BetaJSONOutputFormatParam] | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, speed: Optional[Literal["standard", "fast"]] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, temperature: float | Omit = omit, thinking: BetaThinkingConfigParam | Omit = omit, tool_choice: BetaToolChoiceParam | Omit = omit, tools: Iterable[BetaToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaMessage | Stream[BetaRawMessageStreamEvent]: """ Send a structured list of input messages with text and/or image content, and the model will generate the next message in the conversation. The Messages API can be used for either single queries or stateless multi-turn conversations. Learn more about the Messages API in our [user guide](https://platform.claude.com/docs/en/get-started) Args: max_tokens: The maximum number of tokens to generate before stopping. Note that our models may stop _before_ reaching this maximum. This parameter only specifies the absolute maximum number of tokens to generate. Set to `0` to populate the [prompt cache](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pre-warming-the-cache) without generating a response. Different models have different maximum values for this parameter. See [models](https://platform.claude.com/docs/en/about-claude/models/overview) for details. messages: Input messages. Our models are trained to operate on alternating `user` and `assistant` conversational turns. When creating a new `Message`, you specify the prior conversational turns with the `messages` parameter, and the model then generates the next `Message` in the conversation. Consecutive `user` or `assistant` turns in your request will be combined into a single turn. Each input message must be an object with a `role` and `content`. You can specify a single `user`-role message, or you can include multiple `user` and `assistant` messages. If the final message uses the `assistant` role, the response content will continue immediately from the content in that message. This can be used to constrain part of the model's response. Example with a single `user` message: ```json [{ "role": "user", "content": "Hello, Claude" }] ``` Example with multiple conversational turns: ```json [ { "role": "user", "content": "Hello there." }, { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, { "role": "user", "content": "Can you explain LLMs in plain English?" } ] ``` Example with a partially-filled response from Claude: ```json [ { "role": "user", "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" }, { "role": "assistant", "content": "The best answer is (" } ] ``` Each input message `content` may be either a single `string` or an array of content blocks, where each block has a specific `type`. Using a `string` for `content` is shorthand for an array of one content block of type `"text"`. The following input messages are equivalent: ```json { "role": "user", "content": "Hello, Claude" } ``` ```json { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } ``` See [input examples](https://platform.claude.com/docs/en/build-with-claude/working-with-messages). Note that if you want to include a [system prompt](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role), you can use the top-level `system` parameter — there is no `"system"` role for input messages in the Messages API. There is a limit of 100,000 messages in a single request. model: The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. stream: Whether to incrementally stream the response using server-sent events. See [streaming](https://platform.claude.com/docs/en/build-with-claude/streaming) for details. cache_control: Top-level cache control automatically applies a cache_control marker to the last cacheable block in the request. container: Container identifier for reuse across requests. context_management: Context management configuration. This allows you to control how Claude manages context across multiple requests, such as whether to clear function results or not. diagnostics: Request-level diagnostics. Currently carries the previous response id for prompt-cache divergence reporting. fallback_credit_token: The `fallback_credit_token` from a prior refusal's `stop_details`. When a preceding request was refused and returned a `fallback_credit_token`, pass that code here on the retry to have the retry's cache-creation tokens for the prefix that was warm on the refused model billed at the cache-read rate. Must be redeemed by the same organization and workspace, with the same request body (optionally extended by one appended `assistant` message whose content is the partial text — with any trailing whitespace stripped from the final text block — and paired server-tool blocks streamed before the refusal; the appended-assistant form is not available for requests with `output_format` set or forced `tool_choice`), on an eligible fallback model, on the same platform, and within 5 minutes of the refusal; a mismatch is a 400. A token minted mid-server-tool-loop whose partial content was continuable may only be redeemed with the appended-assistant form — if an exact-body retry is rejected with a 400 saying the token must be redeemed by continuing the partial response, retry with the appended-assistant form instead. When the appended-assistant form is used on a model that otherwise disallows assistant-turn prefill, this token also authorizes that one prefill. fallbacks: Opt-in server-side retry on one or more substitute models when the requested model declines for policy reasons. Tried in order: if the first entry also declines, the second is tried, and so on. The string "default" requests the requested model's server-defined default fallback configuration. inference_geo: Specifies the geographic region for inference processing. If not specified, the workspace's `default_inference_geo` is used. mcp_servers: MCP servers to be utilized in this request metadata: An object describing metadata about the request. output_config: Configuration options for the model's output, such as the output format. output_format: Deprecated: Use `output_config.format` instead. See [structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs) A schema to specify Claude's output format in responses. This parameter will be removed in a future release. service_tier: Determines whether to use priority capacity (if available) or standard capacity for this request. Anthropic offers different levels of service for your API requests. See [service-tiers](https://platform.claude.com/docs/en/api/service-tiers) for details. speed: Inference speed mode. `fast` provides significantly faster output token generation at premium pricing. Not all models support `fast`; invalid combinations are rejected at create time. stop_sequences: Custom text sequences that will cause the model to stop generating. Our models will normally stop when they have naturally completed their turn, which will result in a response `stop_reason` of `"end_turn"`. If you want the model to stop generating when it encounters custom strings of text, you can use the `stop_sequences` parameter. If the model encounters one of the custom sequences, the response `stop_reason` value will be `"stop_sequence"` and the response `stop_sequence` value will contain the matched stop sequence. system: System prompt. A system prompt is a way of providing context and instructions to Claude, such as specifying a particular goal or role. See our [guide to system prompts](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role). temperature: Amount of randomness injected into the response. Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` for analytical / multiple choice, and closer to `1.0` for creative and generative tasks. Note that even with `temperature` of `0.0`, the results will not be fully deterministic. thinking: Configuration for enabling Claude's extended thinking. When enabled, responses include `thinking` content blocks showing Claude's thinking process before the final answer. Requires a minimum budget of 1,024 tokens and counts towards your `max_tokens` limit. See [extended thinking](https://platform.claude.com/docs/en/build-with-claude/extended-thinking) for details. tool_choice: How the model should use the provided tools. The model can use a specific tool, any available tool, decide by itself, or not use tools at all. tools: Definitions of tools that the model may use. If you include `tools` in your API request, the model may return `tool_use` content blocks that represent the model's use of those tools. You can then run those tools using the tool input generated by the model and then optionally return results back to the model using `tool_result` content blocks. There are two types of tools: **client tools** and **server tools**. The behavior described below applies to client tools. For [server tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools), see their individual documentation as each has its own behavior (e.g., the [web search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool)). Each tool definition includes: - `name`: Name of the tool. - `description`: Optional, but strongly-recommended description of the tool. - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the tool `input` shape that the model will produce in `tool_use` output content blocks. For example, if you defined `tools` as: ```json [ { "name": "get_stock_price", "description": "Get the current stock price for a given ticker symbol.", "input_schema": { "type": "object", "properties": { "ticker": { "type": "string", "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." } }, "required": ["ticker"] } } ] ``` And then asked the model "What's the S&P 500 at today?", the model might produce `tool_use` content blocks in the response like this: ```json [ { "type": "tool_use", "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "name": "get_stock_price", "input": { "ticker": "^GSPC" } } ] ``` You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an input, and return the following back to the model in a subsequent `user` message: ```json [ { "type": "tool_result", "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "content": "259.75 USD" } ] ``` Tools can be used for workflows that include running client-side tools and functions, or more generally whenever you want the model to produce a particular JSON structure of output. See our [guide](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview) for more details. top_k: Only sample from the top K options for each subsequent token. Used to remove "long tail" low probability responses. [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). Recommended for advanced use cases only. top_p: Use nucleus sampling. In nucleus sampling, we compute the cumulative distribution over all the options for each subsequent token in decreasing probability order and cut it off once it reaches a particular probability specified by `top_p`. Recommended for advanced use cases only. betas: Optional header to specify the beta version(s) you want to use. user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a party other than your organization. Requires the `user-profiles` beta header. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ ... @required_args(["max_tokens", "messages", "model"], ["max_tokens", "messages", "model", "stream"]) def create( self, *, max_tokens: int, messages: Iterable[BetaMessageParam], model: ModelParam, cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, container: Optional[message_create_params.Container] | Omit = omit, context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, diagnostics: Optional[BetaDiagnosticsParam] | Omit = omit, fallback_credit_token: Optional[message_create_params.FallbackCreditToken] | Omit = omit, fallbacks: Optional[BetaFallbacksParam] | Omit = omit, inference_geo: Optional[str] | Omit = omit, mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, metadata: BetaMetadataParam | Omit = omit, output_config: BetaOutputConfigParam | Omit = omit, output_format: Optional[BetaJSONOutputFormatParam] | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, speed: Optional[Literal["standard", "fast"]] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, stream: Literal[False] | Literal[True] | Omit = omit, system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, temperature: float | Omit = omit, thinking: BetaThinkingConfigParam | Omit = omit, tool_choice: BetaToolChoiceParam | Omit = omit, tools: Iterable[BetaToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaMessage | Stream[BetaRawMessageStreamEvent]: validate_output_format(output_format) _validate_output_config_conflict(output_config, output_format) _warn_output_format_deprecated(output_format) if not stream and not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: timeout = self._client._calculate_nonstreaming_timeout( max_tokens, MODEL_NONSTREAMING_TOKENS.get(model, None) ) if model in DEPRECATED_MODELS: warnings.warn( f"The model '{model}' is deprecated and will reach end-of-life on {DEPRECATED_MODELS[model]}.\nPlease migrate to a newer model. Visit https://docs.anthropic.com/en/docs/resources/model-deprecations for more information.", DeprecationWarning, stacklevel=3, ) if model in MODELS_TO_WARN_WITH_THINKING_ENABLED and thinking and thinking["type"] == "enabled": warnings.warn( f"Using Claude with {model} and 'thinking.type=enabled' is deprecated. Use 'thinking.type=adaptive' instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking", UserWarning, stacklevel=3, ) merged_output_config = _merge_output_configs(output_config, output_format) extra_headers = merge_headers( strip_not_given( { "anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given, "anthropic-user-profile-id": user_profile_id, } ), _stainless_helper_header(tools, messages), extra_headers or {}, ) return self._post( "/v1/messages?beta=true", body=maybe_transform( { "max_tokens": max_tokens, "messages": messages, "model": model, "cache_control": cache_control, "container": container, "context_management": context_management, "diagnostics": diagnostics, "fallback_credit_token": fallback_credit_token, "fallbacks": fallbacks, "inference_geo": inference_geo, "mcp_servers": mcp_servers, "metadata": metadata, "output_config": merged_output_config, "output_format": omit, "service_tier": service_tier, "speed": speed, "stop_sequences": stop_sequences, "stream": stream, "system": system, "temperature": temperature, "thinking": thinking, "tool_choice": tool_choice, "tools": tools, "top_k": top_k, "top_p": top_p, }, message_create_params.MessageCreateParamsStreaming if stream else message_create_params.MessageCreateParamsNonStreaming, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaMessage, stream=stream or False, stream_cls=Stream[BetaRawMessageStreamEvent], ) def parse( self, *, max_tokens: int, messages: Iterable[BetaMessageParam], model: ModelParam, cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, container: Optional[message_create_params.Container] | Omit = omit, context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, diagnostics: Optional[BetaDiagnosticsParam] | Omit = omit, fallback_credit_token: Optional[message_create_params.FallbackCreditToken] | Omit = omit, fallbacks: Optional[BetaFallbacksParam] | Omit = omit, inference_geo: Optional[str] | Omit = omit, mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, metadata: BetaMetadataParam | Omit = omit, output_config: BetaOutputConfigParam | Omit = omit, output_format: Optional[type[ResponseFormatT]] | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, speed: Optional[Literal["standard", "fast"]] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, stream: Literal[False] | Literal[True] | Omit = omit, system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, temperature: float | Omit = omit, thinking: BetaThinkingConfigParam | Omit = omit, tool_choice: BetaToolChoiceParam | Omit = omit, tools: Iterable[BetaToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ParsedBetaMessage[ResponseFormatT]: _validate_output_config_conflict(output_config, output_format) _warn_output_format_deprecated(output_format) if not stream and not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: timeout = self._client._calculate_nonstreaming_timeout( max_tokens, MODEL_NONSTREAMING_TOKENS.get(model, None) ) if model in DEPRECATED_MODELS: warnings.warn( f"The model '{model}' is deprecated and will reach end-of-life on {DEPRECATED_MODELS[model]}.\nPlease migrate to a newer model. Visit https://docs.anthropic.com/en/docs/resources/model-deprecations for more information.", DeprecationWarning, stacklevel=3, ) if model in MODELS_TO_WARN_WITH_THINKING_ENABLED and thinking and thinking["type"] == "enabled": warnings.warn( f"Using Claude with {model} and 'thinking.type=enabled' is deprecated. Use 'thinking.type=adaptive' instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking", UserWarning, stacklevel=3, ) betas = [beta for beta in betas] if is_given(betas) else [] if "structured-outputs-2025-12-15" not in betas: # Ensure structured outputs beta is included for parse method betas.append("structured-outputs-2025-12-15") extra_headers = merge_headers( _helper_header("beta.messages.parse"), strip_not_given( { "anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else NOT_GIVEN, "anthropic-user-profile-id": user_profile_id, } ), _stainless_helper_header(tools, messages), extra_headers or {}, ) if is_given(output_format) and output_format is not None: adapted_type: TypeAdapter[ResponseFormatT] = TypeAdapter(output_format) try: schema = adapted_type.json_schema() transformed_output_format = BetaJSONOutputFormatParam( schema=transform_schema(schema), type="json_schema" ) except pydantic.errors.PydanticSchemaGenerationError as e: raise TypeError( ( "Could not generate JSON schema for the given `output_format` type. " "Use a type that works with `pydantic.TypeAdapter`" ) ) from e merged_output_config = _merge_output_configs(output_config, transformed_output_format) else: merged_output_config = output_config def parser(response: BetaMessage) -> ParsedBetaMessage[ResponseFormatT]: return parse_beta_response( response=response, output_format=cast( ResponseFormatT, output_format if is_given(output_format) and output_format is not None else NOT_GIVEN, ), ) return self._post( "/v1/messages?beta=true", body=maybe_transform( { "max_tokens": max_tokens, "messages": messages, "model": model, "cache_control": cache_control, "container": container, "context_management": context_management, "diagnostics": diagnostics, "fallback_credit_token": fallback_credit_token, "fallbacks": fallbacks, "inference_geo": inference_geo, "mcp_servers": mcp_servers, "metadata": metadata, "output_config": merged_output_config, "output_format": omit, "service_tier": service_tier, "speed": speed, "stop_sequences": stop_sequences, "stream": stream, "system": system, "temperature": temperature, "thinking": thinking, "tool_choice": tool_choice, "tools": tools, "top_k": top_k, "top_p": top_p, }, message_create_params.MessageCreateParamsNonStreaming, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, post_parser=parser, ), cast_to=cast(Type[ParsedBetaMessage[ResponseFormatT]], BetaMessage), stream=False, ) @overload def tool_runner( self, *, max_tokens: int, messages: Iterable[BetaMessageParam], model: ModelParam, tools: Iterable[BetaRunnableTool | BetaToolUnionParam], compaction_control: CompactionControl | Omit = omit, cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, container: Optional[message_create_params.Container] | Omit = omit, context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, diagnostics: Optional[BetaDiagnosticsParam] | Omit = omit, fallback_credit_token: Optional[message_create_params.FallbackCreditToken] | Omit = omit, fallbacks: Optional[BetaFallbacksParam] | Omit = omit, inference_geo: Optional[str] | Omit = omit, max_iterations: int | Omit = omit, mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, metadata: BetaMetadataParam | Omit = omit, output_config: BetaOutputConfigParam | Omit = omit, output_format: Optional[type[ResponseFormatT]] | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, speed: Optional[Literal["standard", "fast"]] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, stream: Literal[False] | Omit = omit, system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, temperature: float | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, thinking: BetaThinkingConfigParam | Omit = omit, tool_choice: BetaToolChoiceParam | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> BetaToolRunner[ResponseFormatT]: ... @overload def tool_runner( self, *, max_tokens: int, messages: Iterable[BetaMessageParam], model: ModelParam, tools: Iterable[BetaRunnableTool | BetaToolUnionParam], cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, compaction_control: CompactionControl | Omit = omit, stream: Literal[True], max_iterations: int | Omit = omit, container: Optional[message_create_params.Container] | Omit = omit, context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, diagnostics: Optional[BetaDiagnosticsParam] | Omit = omit, fallback_credit_token: Optional[message_create_params.FallbackCreditToken] | Omit = omit, fallbacks: Optional[BetaFallbacksParam] | Omit = omit, inference_geo: Optional[str] | Omit = omit, mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, metadata: BetaMetadataParam | Omit = omit, output_config: BetaOutputConfigParam | Omit = omit, output_format: Optional[type[ResponseFormatT]] | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, speed: Optional[Literal["standard", "fast"]] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, temperature: float | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, thinking: BetaThinkingConfigParam | Omit = omit, tool_choice: BetaToolChoiceParam | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> BetaStreamingToolRunner[ResponseFormatT]: ... @overload def tool_runner( self, *, max_tokens: int, messages: Iterable[BetaMessageParam], model: ModelParam, tools: Iterable[BetaRunnableTool | BetaToolUnionParam], compaction_control: CompactionControl | Omit = omit, stream: bool, max_iterations: int | Omit = omit, cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, container: Optional[message_create_params.Container] | Omit = omit, context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, diagnostics: Optional[BetaDiagnosticsParam] | Omit = omit, fallback_credit_token: Optional[message_create_params.FallbackCreditToken] | Omit = omit, fallbacks: Optional[BetaFallbacksParam] | Omit = omit, inference_geo: Optional[str] | Omit = omit, mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, metadata: BetaMetadataParam | Omit = omit, output_config: BetaOutputConfigParam | Omit = omit, output_format: Optional[type[ResponseFormatT]] | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, speed: Optional[Literal["standard", "fast"]] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, temperature: float | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, thinking: BetaThinkingConfigParam | Omit = omit, tool_choice: BetaToolChoiceParam | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> BetaStreamingToolRunner[ResponseFormatT] | BetaToolRunner[ResponseFormatT]: ... def tool_runner( self, *, max_tokens: int, messages: Iterable[BetaMessageParam], model: ModelParam, tools: Iterable[BetaRunnableTool | BetaToolUnionParam], compaction_control: CompactionControl | Omit = omit, max_iterations: int | Omit = omit, cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, container: Optional[message_create_params.Container] | Omit = omit, context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, diagnostics: Optional[BetaDiagnosticsParam] | Omit = omit, fallback_credit_token: Optional[message_create_params.FallbackCreditToken] | Omit = omit, fallbacks: Optional[BetaFallbacksParam] | Omit = omit, inference_geo: Optional[str] | Omit = omit, mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, metadata: BetaMetadataParam | Omit = omit, output_config: BetaOutputConfigParam | Omit = omit, output_format: Optional[type[ResponseFormatT]] | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, speed: Optional[Literal["standard", "fast"]] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, stream: bool | Omit = omit, system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, temperature: float | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, thinking: BetaThinkingConfigParam | Omit = omit, tool_choice: BetaToolChoiceParam | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> BetaStreamingToolRunner[ResponseFormatT] | BetaToolRunner[ResponseFormatT]: """Create a Message stream""" _validate_output_config_conflict(output_config, output_format) _warn_output_format_deprecated(output_format) if model in DEPRECATED_MODELS: warnings.warn( f"The model '{model}' is deprecated and will reach end-of-life on {DEPRECATED_MODELS[model]}.\nPlease migrate to a newer model. Visit https://docs.anthropic.com/en/docs/resources/model-deprecations for more information.", DeprecationWarning, stacklevel=3, ) if model in MODELS_TO_WARN_WITH_THINKING_ENABLED and thinking and thinking["type"] == "enabled": warnings.warn( f"Using Claude with {model} and 'thinking.type=enabled' is deprecated. Use 'thinking.type=adaptive' instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking", UserWarning, stacklevel=3, ) if model in MODELS_TO_WARN_WITH_THINKING_ENABLED and thinking and thinking["type"] == "enabled": warnings.warn( f"Using Claude with {model} and 'thinking.type=enabled' is deprecated. Use 'thinking.type=adaptive' instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking", UserWarning, stacklevel=3, ) extra_headers = merge_headers( _helper_header("BetaToolRunner"), strip_not_given( { "anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else NOT_GIVEN, "anthropic-user-profile-id": user_profile_id, } ), _stainless_helper_header(tools, messages), extra_headers or {}, ) runnable_tools: list[BetaRunnableTool] = [] raw_tools: list[BetaToolUnionParam] = [] for tool in tools: if isinstance(tool, (BetaFunctionTool, BetaBuiltinFunctionTool)): runnable_tools.append(tool) else: raw_tools.append(tool) params = cast( message_create_params.ParseMessageCreateParamsBase[ResponseFormatT], { "max_tokens": max_tokens, "messages": messages, "model": model, "cache_control": cache_control, "container": container, "context_management": context_management, "diagnostics": diagnostics, "fallback_credit_token": fallback_credit_token, "fallbacks": fallbacks, "inference_geo": inference_geo, "mcp_servers": mcp_servers, "metadata": metadata, "output_config": output_config, "output_format": output_format, "service_tier": service_tier, "speed": speed, "stop_sequences": stop_sequences, "system": system, "temperature": temperature, "thinking": thinking, "tool_choice": tool_choice, "tools": [*[tool.to_dict() for tool in runnable_tools], *raw_tools], "top_k": top_k, "top_p": top_p, }, ) if stream: return BetaStreamingToolRunner[ResponseFormatT]( tools=runnable_tools, params=params, options={ "extra_headers": extra_headers, "extra_query": extra_query, "extra_body": extra_body, "timeout": timeout, }, client=cast("Anthropic", self._client), max_iterations=max_iterations if is_given(max_iterations) else None, compaction_control=compaction_control if is_given(compaction_control) else None, ) return BetaToolRunner[ResponseFormatT]( tools=runnable_tools, params=params, options={ "extra_headers": extra_headers, "extra_query": extra_query, "extra_body": extra_body, "timeout": timeout, }, client=cast("Anthropic", self._client), max_iterations=max_iterations if is_given(max_iterations) else None, compaction_control=compaction_control if is_given(compaction_control) else None, ) def stream( self, *, max_tokens: int, messages: Iterable[BetaMessageParam], model: ModelParam, cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, container: Optional[message_create_params.Container] | Omit = omit, context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, diagnostics: Optional[BetaDiagnosticsParam] | Omit = omit, fallback_credit_token: Optional[message_create_params.FallbackCreditToken] | Omit = omit, fallbacks: Optional[BetaFallbacksParam] | Omit = omit, inference_geo: Optional[str] | Omit = omit, mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, metadata: BetaMetadataParam | Omit = omit, output_config: BetaOutputConfigParam | Omit = omit, output_format: None | BetaJSONOutputFormatParam | type[ResponseFormatT] | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, speed: Optional[Literal["standard", "fast"]] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, temperature: float | Omit = omit, thinking: BetaThinkingConfigParam | Omit = omit, tool_choice: BetaToolChoiceParam | Omit = omit, tools: Iterable[BetaToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> BetaMessageStreamManager[ResponseFormatT]: _validate_output_config_conflict(output_config, output_format) _warn_output_format_deprecated(output_format) if model in DEPRECATED_MODELS: warnings.warn( f"The model '{model}' is deprecated and will reach end-of-life on {DEPRECATED_MODELS[model]}.\nPlease migrate to a newer model. Visit https://docs.anthropic.com/en/docs/resources/model-deprecations for more information.", DeprecationWarning, stacklevel=3, ) if model in MODELS_TO_WARN_WITH_THINKING_ENABLED and thinking and thinking["type"] == "enabled": warnings.warn( f"Using Claude with {model} and 'thinking.type=enabled' is deprecated. Use 'thinking.type=adaptive' instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking", UserWarning, stacklevel=3, ) """Create a Message stream""" extra_headers = merge_headers( { _STAINLESS_HELPER_METHOD_HEADER: _HELPER_METHOD_STREAM, _STAINLESS_STREAM_HELPER_HEADER: "beta.messages", }, strip_not_given( { "anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else NOT_GIVEN, "anthropic-user-profile-id": user_profile_id, } ), _stainless_helper_header(tools, messages), extra_headers or {}, ) transformed_output_format: BetaJSONOutputFormatParam | Omit = omit if is_dict(output_format): transformed_output_format = cast(BetaJSONOutputFormatParam, output_format) elif is_given(output_format) and output_format is not None: adapted_type: TypeAdapter[ResponseFormatT] = TypeAdapter(output_format) try: schema = adapted_type.json_schema() transformed_output_format = BetaJSONOutputFormatParam( schema=transform_schema(schema), type="json_schema" ) except pydantic.errors.PydanticSchemaGenerationError as e: raise TypeError( ( "Could not generate JSON schema for the given `output_format` type. " "Use a type that works with `pydantic.TypeAdapter`" ) ) from e merged_output_config = _merge_output_configs(output_config, transformed_output_format) make_request = partial( self._post, "/v1/messages?beta=true", body=maybe_transform( { "max_tokens": max_tokens, "messages": messages, "model": model, "cache_control": cache_control, "metadata": metadata, "output_config": merged_output_config, "output_format": omit, "container": container, "context_management": context_management, "diagnostics": diagnostics, "fallback_credit_token": fallback_credit_token, "fallbacks": fallbacks, "inference_geo": inference_geo, "mcp_servers": mcp_servers, "service_tier": service_tier, "speed": speed, "stop_sequences": stop_sequences, "system": system, "temperature": temperature, "thinking": thinking, "top_k": top_k, "top_p": top_p, "tools": tools, "tool_choice": tool_choice, "stream": True, }, message_create_params.MessageCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaMessage, stream=True, stream_cls=Stream[BetaRawMessageStreamEvent], ) return BetaMessageStreamManager( make_request, output_format=NOT_GIVEN if is_dict(output_format) else cast(ResponseFormatT, output_format), ) def count_tokens( self, *, messages: Iterable[BetaMessageParam], model: ModelParam, cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, output_config: BetaOutputConfigParam | Omit = omit, output_format: Optional[BetaJSONOutputFormatParam] | Omit = omit, speed: Optional[Literal["standard", "fast"]] | Omit = omit, system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, thinking: BetaThinkingConfigParam | Omit = omit, tool_choice: BetaToolChoiceParam | Omit = omit, tools: Iterable[message_count_tokens_params.Tool] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaMessageTokensCount: """ Count the number of tokens in a Message. The Token Count API can be used to count the number of tokens in a Message, including tools, images, and documents, without creating it. Learn more about token counting in our [user guide](https://platform.claude.com/docs/en/build-with-claude/token-counting) Args: messages: Input messages. Our models are trained to operate on alternating `user` and `assistant` conversational turns. When creating a new `Message`, you specify the prior conversational turns with the `messages` parameter, and the model then generates the next `Message` in the conversation. Consecutive `user` or `assistant` turns in your request will be combined into a single turn. Each input message must be an object with a `role` and `content`. You can specify a single `user`-role message, or you can include multiple `user` and `assistant` messages. If the final message uses the `assistant` role, the response content will continue immediately from the content in that message. This can be used to constrain part of the model's response. Example with a single `user` message: ```json [{ "role": "user", "content": "Hello, Claude" }] ``` Example with multiple conversational turns: ```json [ { "role": "user", "content": "Hello there." }, { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, { "role": "user", "content": "Can you explain LLMs in plain English?" } ] ``` Example with a partially-filled response from Claude: ```json [ { "role": "user", "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" }, { "role": "assistant", "content": "The best answer is (" } ] ``` Each input message `content` may be either a single `string` or an array of content blocks, where each block has a specific `type`. Using a `string` for `content` is shorthand for an array of one content block of type `"text"`. The following input messages are equivalent: ```json { "role": "user", "content": "Hello, Claude" } ``` ```json { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } ``` See [input examples](https://platform.claude.com/docs/en/build-with-claude/working-with-messages). Note that if you want to include a [system prompt](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role), you can use the top-level `system` parameter — there is no `"system"` role for input messages in the Messages API. There is a limit of 100,000 messages in a single request. model: The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. cache_control: Top-level cache control automatically applies a cache_control marker to the last cacheable block in the request. context_management: Context management configuration. This allows you to control how Claude manages context across multiple requests, such as whether to clear function results or not. mcp_servers: MCP servers to be utilized in this request output_config: Configuration options for the model's output, such as the output format. output_format: Deprecated: Use `output_config.format` instead. See [structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs) A schema to specify Claude's output format in responses. This parameter will be removed in a future release. speed: Inference speed mode. `fast` provides significantly faster output token generation at premium pricing. Not all models support `fast`; invalid combinations are rejected at create time. system: System prompt. A system prompt is a way of providing context and instructions to Claude, such as specifying a particular goal or role. See our [guide to system prompts](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role). thinking: Configuration for enabling Claude's extended thinking. When enabled, responses include `thinking` content blocks showing Claude's thinking process before the final answer. Requires a minimum budget of 1,024 tokens and counts towards your `max_tokens` limit. See [extended thinking](https://platform.claude.com/docs/en/build-with-claude/extended-thinking) for details. tool_choice: How the model should use the provided tools. The model can use a specific tool, any available tool, decide by itself, or not use tools at all. tools: Definitions of tools that the model may use. If you include `tools` in your API request, the model may return `tool_use` content blocks that represent the model's use of those tools. You can then run those tools using the tool input generated by the model and then optionally return results back to the model using `tool_result` content blocks. There are two types of tools: **client tools** and **server tools**. The behavior described below applies to client tools. For [server tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools), see their individual documentation as each has its own behavior (e.g., the [web search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool)). Each tool definition includes: - `name`: Name of the tool. - `description`: Optional, but strongly-recommended description of the tool. - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the tool `input` shape that the model will produce in `tool_use` output content blocks. For example, if you defined `tools` as: ```json [ { "name": "get_stock_price", "description": "Get the current stock price for a given ticker symbol.", "input_schema": { "type": "object", "properties": { "ticker": { "type": "string", "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." } }, "required": ["ticker"] } } ] ``` And then asked the model "What's the S&P 500 at today?", the model might produce `tool_use` content blocks in the response like this: ```json [ { "type": "tool_use", "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "name": "get_stock_price", "input": { "ticker": "^GSPC" } } ] ``` You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an input, and return the following back to the model in a subsequent `user` message: ```json [ { "type": "tool_result", "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "content": "259.75 USD" } ] ``` Tools can be used for workflows that include running client-side tools and functions, or more generally whenever you want the model to produce a particular JSON structure of output. See our [guide](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview) for more details. betas: Optional header to specify the beta version(s) you want to use. user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a party other than your organization. Requires the `user-profiles` beta header. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ _validate_output_config_conflict(output_config, output_format) _warn_output_format_deprecated(output_format) merged_output_config = _merge_output_configs(output_config, output_format) extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["token-counting-2024-11-01"])) if is_given(betas) else not_given, "anthropic-user-profile-id": user_profile_id, } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "token-counting-2024-11-01", **(extra_headers or {})} return self._post( "/v1/messages/count_tokens?beta=true", body=maybe_transform( { "messages": messages, "model": model, "cache_control": cache_control, "context_management": context_management, "mcp_servers": mcp_servers, "output_config": merged_output_config, "output_format": omit, "speed": speed, "system": system, "thinking": thinking, "tool_choice": tool_choice, "tools": tools, }, message_count_tokens_params.MessageCountTokensParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaMessageTokensCount, ) class AsyncMessages(AsyncAPIResource): @cached_property def batches(self) -> AsyncBatches: return AsyncBatches(self._client) @cached_property def with_raw_response(self) -> AsyncMessagesWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncMessagesWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncMessagesWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncMessagesWithStreamingResponse(self) @overload async def create( self, *, max_tokens: int, messages: Iterable[BetaMessageParam], model: ModelParam, cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, container: Optional[message_create_params.Container] | Omit = omit, context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, diagnostics: Optional[BetaDiagnosticsParam] | Omit = omit, fallback_credit_token: Optional[message_create_params.FallbackCreditToken] | Omit = omit, fallbacks: Optional[BetaFallbacksParam] | Omit = omit, inference_geo: Optional[str] | Omit = omit, mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, metadata: BetaMetadataParam | Omit = omit, output_config: BetaOutputConfigParam | Omit = omit, output_format: Optional[BetaJSONOutputFormatParam] | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, speed: Optional[Literal["standard", "fast"]] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, stream: Literal[False] | Omit = omit, system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, temperature: float | Omit = omit, thinking: BetaThinkingConfigParam | Omit = omit, tool_choice: BetaToolChoiceParam | Omit = omit, tools: Iterable[BetaToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaMessage: """ Send a structured list of input messages with text and/or image content, and the model will generate the next message in the conversation. The Messages API can be used for either single queries or stateless multi-turn conversations. Learn more about the Messages API in our [user guide](https://platform.claude.com/docs/en/get-started) Args: max_tokens: The maximum number of tokens to generate before stopping. Note that our models may stop _before_ reaching this maximum. This parameter only specifies the absolute maximum number of tokens to generate. Set to `0` to populate the [prompt cache](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pre-warming-the-cache) without generating a response. Different models have different maximum values for this parameter. See [models](https://platform.claude.com/docs/en/about-claude/models/overview) for details. messages: Input messages. Our models are trained to operate on alternating `user` and `assistant` conversational turns. When creating a new `Message`, you specify the prior conversational turns with the `messages` parameter, and the model then generates the next `Message` in the conversation. Consecutive `user` or `assistant` turns in your request will be combined into a single turn. Each input message must be an object with a `role` and `content`. You can specify a single `user`-role message, or you can include multiple `user` and `assistant` messages. If the final message uses the `assistant` role, the response content will continue immediately from the content in that message. This can be used to constrain part of the model's response. Example with a single `user` message: ```json [{ "role": "user", "content": "Hello, Claude" }] ``` Example with multiple conversational turns: ```json [ { "role": "user", "content": "Hello there." }, { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, { "role": "user", "content": "Can you explain LLMs in plain English?" } ] ``` Example with a partially-filled response from Claude: ```json [ { "role": "user", "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" }, { "role": "assistant", "content": "The best answer is (" } ] ``` Each input message `content` may be either a single `string` or an array of content blocks, where each block has a specific `type`. Using a `string` for `content` is shorthand for an array of one content block of type `"text"`. The following input messages are equivalent: ```json { "role": "user", "content": "Hello, Claude" } ``` ```json { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } ``` See [input examples](https://platform.claude.com/docs/en/build-with-claude/working-with-messages). Note that if you want to include a [system prompt](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role), you can use the top-level `system` parameter — there is no `"system"` role for input messages in the Messages API. There is a limit of 100,000 messages in a single request. model: The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. cache_control: Top-level cache control automatically applies a cache_control marker to the last cacheable block in the request. container: Container identifier for reuse across requests. context_management: Context management configuration. This allows you to control how Claude manages context across multiple requests, such as whether to clear function results or not. diagnostics: Request-level diagnostics. Currently carries the previous response id for prompt-cache divergence reporting. fallback_credit_token: The `fallback_credit_token` from a prior refusal's `stop_details`. When a preceding request was refused and returned a `fallback_credit_token`, pass that code here on the retry to have the retry's cache-creation tokens for the prefix that was warm on the refused model billed at the cache-read rate. Must be redeemed by the same organization and workspace, with the same request body (optionally extended by one appended `assistant` message whose content is the partial text — with any trailing whitespace stripped from the final text block — and paired server-tool blocks streamed before the refusal; the appended-assistant form is not available for requests with `output_format` set or forced `tool_choice`), on an eligible fallback model, on the same platform, and within 5 minutes of the refusal; a mismatch is a 400. A token minted mid-server-tool-loop whose partial content was continuable may only be redeemed with the appended-assistant form — if an exact-body retry is rejected with a 400 saying the token must be redeemed by continuing the partial response, retry with the appended-assistant form instead. When the appended-assistant form is used on a model that otherwise disallows assistant-turn prefill, this token also authorizes that one prefill. fallbacks: Opt-in server-side retry on one or more substitute models when the requested model declines for policy reasons. Tried in order: if the first entry also declines, the second is tried, and so on. The string "default" requests the requested model's server-defined default fallback configuration. inference_geo: Specifies the geographic region for inference processing. If not specified, the workspace's `default_inference_geo` is used. mcp_servers: MCP servers to be utilized in this request metadata: An object describing metadata about the request. output_config: Configuration options for the model's output, such as the output format. output_format: Deprecated: Use `output_config.format` instead. See [structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs) A schema to specify Claude's output format in responses. This parameter will be removed in a future release. service_tier: Determines whether to use priority capacity (if available) or standard capacity for this request. Anthropic offers different levels of service for your API requests. See [service-tiers](https://platform.claude.com/docs/en/api/service-tiers) for details. speed: Inference speed mode. `fast` provides significantly faster output token generation at premium pricing. Not all models support `fast`; invalid combinations are rejected at create time. stop_sequences: Custom text sequences that will cause the model to stop generating. Our models will normally stop when they have naturally completed their turn, which will result in a response `stop_reason` of `"end_turn"`. If you want the model to stop generating when it encounters custom strings of text, you can use the `stop_sequences` parameter. If the model encounters one of the custom sequences, the response `stop_reason` value will be `"stop_sequence"` and the response `stop_sequence` value will contain the matched stop sequence. stream: Whether to incrementally stream the response using server-sent events. See [streaming](https://platform.claude.com/docs/en/build-with-claude/streaming) for details. system: System prompt. A system prompt is a way of providing context and instructions to Claude, such as specifying a particular goal or role. See our [guide to system prompts](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role). temperature: Amount of randomness injected into the response. Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` for analytical / multiple choice, and closer to `1.0` for creative and generative tasks. Note that even with `temperature` of `0.0`, the results will not be fully deterministic. thinking: Configuration for enabling Claude's extended thinking. When enabled, responses include `thinking` content blocks showing Claude's thinking process before the final answer. Requires a minimum budget of 1,024 tokens and counts towards your `max_tokens` limit. See [extended thinking](https://platform.claude.com/docs/en/build-with-claude/extended-thinking) for details. tool_choice: How the model should use the provided tools. The model can use a specific tool, any available tool, decide by itself, or not use tools at all. tools: Definitions of tools that the model may use. If you include `tools` in your API request, the model may return `tool_use` content blocks that represent the model's use of those tools. You can then run those tools using the tool input generated by the model and then optionally return results back to the model using `tool_result` content blocks. There are two types of tools: **client tools** and **server tools**. The behavior described below applies to client tools. For [server tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools), see their individual documentation as each has its own behavior (e.g., the [web search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool)). Each tool definition includes: - `name`: Name of the tool. - `description`: Optional, but strongly-recommended description of the tool. - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the tool `input` shape that the model will produce in `tool_use` output content blocks. For example, if you defined `tools` as: ```json [ { "name": "get_stock_price", "description": "Get the current stock price for a given ticker symbol.", "input_schema": { "type": "object", "properties": { "ticker": { "type": "string", "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." } }, "required": ["ticker"] } } ] ``` And then asked the model "What's the S&P 500 at today?", the model might produce `tool_use` content blocks in the response like this: ```json [ { "type": "tool_use", "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "name": "get_stock_price", "input": { "ticker": "^GSPC" } } ] ``` You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an input, and return the following back to the model in a subsequent `user` message: ```json [ { "type": "tool_result", "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "content": "259.75 USD" } ] ``` Tools can be used for workflows that include running client-side tools and functions, or more generally whenever you want the model to produce a particular JSON structure of output. See our [guide](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview) for more details. top_k: Only sample from the top K options for each subsequent token. Used to remove "long tail" low probability responses. [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). Recommended for advanced use cases only. top_p: Use nucleus sampling. In nucleus sampling, we compute the cumulative distribution over all the options for each subsequent token in decreasing probability order and cut it off once it reaches a particular probability specified by `top_p`. Recommended for advanced use cases only. betas: Optional header to specify the beta version(s) you want to use. user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a party other than your organization. Requires the `user-profiles` beta header. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ ... @overload async def create( self, *, max_tokens: int, messages: Iterable[BetaMessageParam], model: ModelParam, stream: Literal[True], cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, container: Optional[message_create_params.Container] | Omit = omit, context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, diagnostics: Optional[BetaDiagnosticsParam] | Omit = omit, fallback_credit_token: Optional[message_create_params.FallbackCreditToken] | Omit = omit, fallbacks: Optional[BetaFallbacksParam] | Omit = omit, inference_geo: Optional[str] | Omit = omit, mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, metadata: BetaMetadataParam | Omit = omit, output_config: BetaOutputConfigParam | Omit = omit, output_format: Optional[BetaJSONOutputFormatParam] | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, speed: Optional[Literal["standard", "fast"]] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, temperature: float | Omit = omit, thinking: BetaThinkingConfigParam | Omit = omit, tool_choice: BetaToolChoiceParam | Omit = omit, tools: Iterable[BetaToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncStream[BetaRawMessageStreamEvent]: """ Send a structured list of input messages with text and/or image content, and the model will generate the next message in the conversation. The Messages API can be used for either single queries or stateless multi-turn conversations. Learn more about the Messages API in our [user guide](https://platform.claude.com/docs/en/get-started) Args: max_tokens: The maximum number of tokens to generate before stopping. Note that our models may stop _before_ reaching this maximum. This parameter only specifies the absolute maximum number of tokens to generate. Set to `0` to populate the [prompt cache](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pre-warming-the-cache) without generating a response. Different models have different maximum values for this parameter. See [models](https://platform.claude.com/docs/en/about-claude/models/overview) for details. messages: Input messages. Our models are trained to operate on alternating `user` and `assistant` conversational turns. When creating a new `Message`, you specify the prior conversational turns with the `messages` parameter, and the model then generates the next `Message` in the conversation. Consecutive `user` or `assistant` turns in your request will be combined into a single turn. Each input message must be an object with a `role` and `content`. You can specify a single `user`-role message, or you can include multiple `user` and `assistant` messages. If the final message uses the `assistant` role, the response content will continue immediately from the content in that message. This can be used to constrain part of the model's response. Example with a single `user` message: ```json [{ "role": "user", "content": "Hello, Claude" }] ``` Example with multiple conversational turns: ```json [ { "role": "user", "content": "Hello there." }, { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, { "role": "user", "content": "Can you explain LLMs in plain English?" } ] ``` Example with a partially-filled response from Claude: ```json [ { "role": "user", "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" }, { "role": "assistant", "content": "The best answer is (" } ] ``` Each input message `content` may be either a single `string` or an array of content blocks, where each block has a specific `type`. Using a `string` for `content` is shorthand for an array of one content block of type `"text"`. The following input messages are equivalent: ```json { "role": "user", "content": "Hello, Claude" } ``` ```json { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } ``` See [input examples](https://platform.claude.com/docs/en/build-with-claude/working-with-messages). Note that if you want to include a [system prompt](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role), you can use the top-level `system` parameter — there is no `"system"` role for input messages in the Messages API. There is a limit of 100,000 messages in a single request. model: The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. stream: Whether to incrementally stream the response using server-sent events. See [streaming](https://platform.claude.com/docs/en/build-with-claude/streaming) for details. cache_control: Top-level cache control automatically applies a cache_control marker to the last cacheable block in the request. container: Container identifier for reuse across requests. context_management: Context management configuration. This allows you to control how Claude manages context across multiple requests, such as whether to clear function results or not. diagnostics: Request-level diagnostics. Currently carries the previous response id for prompt-cache divergence reporting. fallback_credit_token: The `fallback_credit_token` from a prior refusal's `stop_details`. When a preceding request was refused and returned a `fallback_credit_token`, pass that code here on the retry to have the retry's cache-creation tokens for the prefix that was warm on the refused model billed at the cache-read rate. Must be redeemed by the same organization and workspace, with the same request body (optionally extended by one appended `assistant` message whose content is the partial text — with any trailing whitespace stripped from the final text block — and paired server-tool blocks streamed before the refusal; the appended-assistant form is not available for requests with `output_format` set or forced `tool_choice`), on an eligible fallback model, on the same platform, and within 5 minutes of the refusal; a mismatch is a 400. A token minted mid-server-tool-loop whose partial content was continuable may only be redeemed with the appended-assistant form — if an exact-body retry is rejected with a 400 saying the token must be redeemed by continuing the partial response, retry with the appended-assistant form instead. When the appended-assistant form is used on a model that otherwise disallows assistant-turn prefill, this token also authorizes that one prefill. fallbacks: Opt-in server-side retry on one or more substitute models when the requested model declines for policy reasons. Tried in order: if the first entry also declines, the second is tried, and so on. The string "default" requests the requested model's server-defined default fallback configuration. inference_geo: Specifies the geographic region for inference processing. If not specified, the workspace's `default_inference_geo` is used. mcp_servers: MCP servers to be utilized in this request metadata: An object describing metadata about the request. output_config: Configuration options for the model's output, such as the output format. output_format: Deprecated: Use `output_config.format` instead. See [structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs) A schema to specify Claude's output format in responses. This parameter will be removed in a future release. service_tier: Determines whether to use priority capacity (if available) or standard capacity for this request. Anthropic offers different levels of service for your API requests. See [service-tiers](https://platform.claude.com/docs/en/api/service-tiers) for details. speed: Inference speed mode. `fast` provides significantly faster output token generation at premium pricing. Not all models support `fast`; invalid combinations are rejected at create time. stop_sequences: Custom text sequences that will cause the model to stop generating. Our models will normally stop when they have naturally completed their turn, which will result in a response `stop_reason` of `"end_turn"`. If you want the model to stop generating when it encounters custom strings of text, you can use the `stop_sequences` parameter. If the model encounters one of the custom sequences, the response `stop_reason` value will be `"stop_sequence"` and the response `stop_sequence` value will contain the matched stop sequence. system: System prompt. A system prompt is a way of providing context and instructions to Claude, such as specifying a particular goal or role. See our [guide to system prompts](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role). temperature: Amount of randomness injected into the response. Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` for analytical / multiple choice, and closer to `1.0` for creative and generative tasks. Note that even with `temperature` of `0.0`, the results will not be fully deterministic. thinking: Configuration for enabling Claude's extended thinking. When enabled, responses include `thinking` content blocks showing Claude's thinking process before the final answer. Requires a minimum budget of 1,024 tokens and counts towards your `max_tokens` limit. See [extended thinking](https://platform.claude.com/docs/en/build-with-claude/extended-thinking) for details. tool_choice: How the model should use the provided tools. The model can use a specific tool, any available tool, decide by itself, or not use tools at all. tools: Definitions of tools that the model may use. If you include `tools` in your API request, the model may return `tool_use` content blocks that represent the model's use of those tools. You can then run those tools using the tool input generated by the model and then optionally return results back to the model using `tool_result` content blocks. There are two types of tools: **client tools** and **server tools**. The behavior described below applies to client tools. For [server tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools), see their individual documentation as each has its own behavior (e.g., the [web search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool)). Each tool definition includes: - `name`: Name of the tool. - `description`: Optional, but strongly-recommended description of the tool. - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the tool `input` shape that the model will produce in `tool_use` output content blocks. For example, if you defined `tools` as: ```json [ { "name": "get_stock_price", "description": "Get the current stock price for a given ticker symbol.", "input_schema": { "type": "object", "properties": { "ticker": { "type": "string", "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." } }, "required": ["ticker"] } } ] ``` And then asked the model "What's the S&P 500 at today?", the model might produce `tool_use` content blocks in the response like this: ```json [ { "type": "tool_use", "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "name": "get_stock_price", "input": { "ticker": "^GSPC" } } ] ``` You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an input, and return the following back to the model in a subsequent `user` message: ```json [ { "type": "tool_result", "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "content": "259.75 USD" } ] ``` Tools can be used for workflows that include running client-side tools and functions, or more generally whenever you want the model to produce a particular JSON structure of output. See our [guide](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview) for more details. top_k: Only sample from the top K options for each subsequent token. Used to remove "long tail" low probability responses. [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). Recommended for advanced use cases only. top_p: Use nucleus sampling. In nucleus sampling, we compute the cumulative distribution over all the options for each subsequent token in decreasing probability order and cut it off once it reaches a particular probability specified by `top_p`. Recommended for advanced use cases only. betas: Optional header to specify the beta version(s) you want to use. user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a party other than your organization. Requires the `user-profiles` beta header. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ ... @overload async def create( self, *, max_tokens: int, messages: Iterable[BetaMessageParam], model: ModelParam, stream: bool, cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, container: Optional[message_create_params.Container] | Omit = omit, context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, diagnostics: Optional[BetaDiagnosticsParam] | Omit = omit, fallback_credit_token: Optional[message_create_params.FallbackCreditToken] | Omit = omit, fallbacks: Optional[BetaFallbacksParam] | Omit = omit, inference_geo: Optional[str] | Omit = omit, mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, metadata: BetaMetadataParam | Omit = omit, output_config: BetaOutputConfigParam | Omit = omit, output_format: Optional[BetaJSONOutputFormatParam] | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, speed: Optional[Literal["standard", "fast"]] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, temperature: float | Omit = omit, thinking: BetaThinkingConfigParam | Omit = omit, tool_choice: BetaToolChoiceParam | Omit = omit, tools: Iterable[BetaToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaMessage | AsyncStream[BetaRawMessageStreamEvent]: """ Send a structured list of input messages with text and/or image content, and the model will generate the next message in the conversation. The Messages API can be used for either single queries or stateless multi-turn conversations. Learn more about the Messages API in our [user guide](https://platform.claude.com/docs/en/get-started) Args: max_tokens: The maximum number of tokens to generate before stopping. Note that our models may stop _before_ reaching this maximum. This parameter only specifies the absolute maximum number of tokens to generate. Set to `0` to populate the [prompt cache](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pre-warming-the-cache) without generating a response. Different models have different maximum values for this parameter. See [models](https://platform.claude.com/docs/en/about-claude/models/overview) for details. messages: Input messages. Our models are trained to operate on alternating `user` and `assistant` conversational turns. When creating a new `Message`, you specify the prior conversational turns with the `messages` parameter, and the model then generates the next `Message` in the conversation. Consecutive `user` or `assistant` turns in your request will be combined into a single turn. Each input message must be an object with a `role` and `content`. You can specify a single `user`-role message, or you can include multiple `user` and `assistant` messages. If the final message uses the `assistant` role, the response content will continue immediately from the content in that message. This can be used to constrain part of the model's response. Example with a single `user` message: ```json [{ "role": "user", "content": "Hello, Claude" }] ``` Example with multiple conversational turns: ```json [ { "role": "user", "content": "Hello there." }, { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, { "role": "user", "content": "Can you explain LLMs in plain English?" } ] ``` Example with a partially-filled response from Claude: ```json [ { "role": "user", "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" }, { "role": "assistant", "content": "The best answer is (" } ] ``` Each input message `content` may be either a single `string` or an array of content blocks, where each block has a specific `type`. Using a `string` for `content` is shorthand for an array of one content block of type `"text"`. The following input messages are equivalent: ```json { "role": "user", "content": "Hello, Claude" } ``` ```json { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } ``` See [input examples](https://platform.claude.com/docs/en/build-with-claude/working-with-messages). Note that if you want to include a [system prompt](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role), you can use the top-level `system` parameter — there is no `"system"` role for input messages in the Messages API. There is a limit of 100,000 messages in a single request. model: The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. stream: Whether to incrementally stream the response using server-sent events. See [streaming](https://platform.claude.com/docs/en/build-with-claude/streaming) for details. cache_control: Top-level cache control automatically applies a cache_control marker to the last cacheable block in the request. container: Container identifier for reuse across requests. context_management: Context management configuration. This allows you to control how Claude manages context across multiple requests, such as whether to clear function results or not. diagnostics: Request-level diagnostics. Currently carries the previous response id for prompt-cache divergence reporting. fallback_credit_token: The `fallback_credit_token` from a prior refusal's `stop_details`. When a preceding request was refused and returned a `fallback_credit_token`, pass that code here on the retry to have the retry's cache-creation tokens for the prefix that was warm on the refused model billed at the cache-read rate. Must be redeemed by the same organization and workspace, with the same request body (optionally extended by one appended `assistant` message whose content is the partial text — with any trailing whitespace stripped from the final text block — and paired server-tool blocks streamed before the refusal; the appended-assistant form is not available for requests with `output_format` set or forced `tool_choice`), on an eligible fallback model, on the same platform, and within 5 minutes of the refusal; a mismatch is a 400. A token minted mid-server-tool-loop whose partial content was continuable may only be redeemed with the appended-assistant form — if an exact-body retry is rejected with a 400 saying the token must be redeemed by continuing the partial response, retry with the appended-assistant form instead. When the appended-assistant form is used on a model that otherwise disallows assistant-turn prefill, this token also authorizes that one prefill. fallbacks: Opt-in server-side retry on one or more substitute models when the requested model declines for policy reasons. Tried in order: if the first entry also declines, the second is tried, and so on. The string "default" requests the requested model's server-defined default fallback configuration. inference_geo: Specifies the geographic region for inference processing. If not specified, the workspace's `default_inference_geo` is used. mcp_servers: MCP servers to be utilized in this request metadata: An object describing metadata about the request. output_config: Configuration options for the model's output, such as the output format. output_format: Deprecated: Use `output_config.format` instead. See [structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs) A schema to specify Claude's output format in responses. This parameter will be removed in a future release. service_tier: Determines whether to use priority capacity (if available) or standard capacity for this request. Anthropic offers different levels of service for your API requests. See [service-tiers](https://platform.claude.com/docs/en/api/service-tiers) for details. speed: Inference speed mode. `fast` provides significantly faster output token generation at premium pricing. Not all models support `fast`; invalid combinations are rejected at create time. stop_sequences: Custom text sequences that will cause the model to stop generating. Our models will normally stop when they have naturally completed their turn, which will result in a response `stop_reason` of `"end_turn"`. If you want the model to stop generating when it encounters custom strings of text, you can use the `stop_sequences` parameter. If the model encounters one of the custom sequences, the response `stop_reason` value will be `"stop_sequence"` and the response `stop_sequence` value will contain the matched stop sequence. system: System prompt. A system prompt is a way of providing context and instructions to Claude, such as specifying a particular goal or role. See our [guide to system prompts](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role). temperature: Amount of randomness injected into the response. Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` for analytical / multiple choice, and closer to `1.0` for creative and generative tasks. Note that even with `temperature` of `0.0`, the results will not be fully deterministic. thinking: Configuration for enabling Claude's extended thinking. When enabled, responses include `thinking` content blocks showing Claude's thinking process before the final answer. Requires a minimum budget of 1,024 tokens and counts towards your `max_tokens` limit. See [extended thinking](https://platform.claude.com/docs/en/build-with-claude/extended-thinking) for details. tool_choice: How the model should use the provided tools. The model can use a specific tool, any available tool, decide by itself, or not use tools at all. tools: Definitions of tools that the model may use. If you include `tools` in your API request, the model may return `tool_use` content blocks that represent the model's use of those tools. You can then run those tools using the tool input generated by the model and then optionally return results back to the model using `tool_result` content blocks. There are two types of tools: **client tools** and **server tools**. The behavior described below applies to client tools. For [server tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools), see their individual documentation as each has its own behavior (e.g., the [web search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool)). Each tool definition includes: - `name`: Name of the tool. - `description`: Optional, but strongly-recommended description of the tool. - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the tool `input` shape that the model will produce in `tool_use` output content blocks. For example, if you defined `tools` as: ```json [ { "name": "get_stock_price", "description": "Get the current stock price for a given ticker symbol.", "input_schema": { "type": "object", "properties": { "ticker": { "type": "string", "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." } }, "required": ["ticker"] } } ] ``` And then asked the model "What's the S&P 500 at today?", the model might produce `tool_use` content blocks in the response like this: ```json [ { "type": "tool_use", "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "name": "get_stock_price", "input": { "ticker": "^GSPC" } } ] ``` You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an input, and return the following back to the model in a subsequent `user` message: ```json [ { "type": "tool_result", "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "content": "259.75 USD" } ] ``` Tools can be used for workflows that include running client-side tools and functions, or more generally whenever you want the model to produce a particular JSON structure of output. See our [guide](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview) for more details. top_k: Only sample from the top K options for each subsequent token. Used to remove "long tail" low probability responses. [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). Recommended for advanced use cases only. top_p: Use nucleus sampling. In nucleus sampling, we compute the cumulative distribution over all the options for each subsequent token in decreasing probability order and cut it off once it reaches a particular probability specified by `top_p`. Recommended for advanced use cases only. betas: Optional header to specify the beta version(s) you want to use. user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a party other than your organization. Requires the `user-profiles` beta header. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ ... @required_args(["max_tokens", "messages", "model"], ["max_tokens", "messages", "model", "stream"]) async def create( self, *, max_tokens: int, messages: Iterable[BetaMessageParam], model: ModelParam, cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, container: Optional[message_create_params.Container] | Omit = omit, context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, diagnostics: Optional[BetaDiagnosticsParam] | Omit = omit, fallback_credit_token: Optional[message_create_params.FallbackCreditToken] | Omit = omit, fallbacks: Optional[BetaFallbacksParam] | Omit = omit, inference_geo: Optional[str] | Omit = omit, mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, metadata: BetaMetadataParam | Omit = omit, output_config: BetaOutputConfigParam | Omit = omit, output_format: Optional[BetaJSONOutputFormatParam] | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, speed: Optional[Literal["standard", "fast"]] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, stream: Literal[False] | Literal[True] | Omit = omit, system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, temperature: float | Omit = omit, thinking: BetaThinkingConfigParam | Omit = omit, tool_choice: BetaToolChoiceParam | Omit = omit, tools: Iterable[BetaToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaMessage | AsyncStream[BetaRawMessageStreamEvent]: validate_output_format(output_format) _validate_output_config_conflict(output_config, output_format) _warn_output_format_deprecated(output_format) if not stream and not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: timeout = self._client._calculate_nonstreaming_timeout( max_tokens, MODEL_NONSTREAMING_TOKENS.get(model, None) ) if model in DEPRECATED_MODELS: warnings.warn( f"The model '{model}' is deprecated and will reach end-of-life on {DEPRECATED_MODELS[model]}.\nPlease migrate to a newer model. Visit https://docs.anthropic.com/en/docs/resources/model-deprecations for more information.", DeprecationWarning, stacklevel=3, ) if model in MODELS_TO_WARN_WITH_THINKING_ENABLED and thinking and thinking["type"] == "enabled": warnings.warn( f"Using Claude with {model} and 'thinking.type=enabled' is deprecated. Use 'thinking.type=adaptive' instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking", UserWarning, stacklevel=3, ) merged_output_config = _merge_output_configs(output_config, output_format) extra_headers = merge_headers( strip_not_given( { "anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given, "anthropic-user-profile-id": user_profile_id, } ), _stainless_helper_header(tools, messages), extra_headers or {}, ) return await self._post( "/v1/messages?beta=true", body=await async_maybe_transform( { "max_tokens": max_tokens, "messages": messages, "model": model, "cache_control": cache_control, "container": container, "context_management": context_management, "diagnostics": diagnostics, "fallback_credit_token": fallback_credit_token, "fallbacks": fallbacks, "inference_geo": inference_geo, "mcp_servers": mcp_servers, "metadata": metadata, "output_config": merged_output_config, "output_format": omit, "service_tier": service_tier, "speed": speed, "stop_sequences": stop_sequences, "stream": stream, "system": system, "temperature": temperature, "thinking": thinking, "tool_choice": tool_choice, "tools": tools, "top_k": top_k, "top_p": top_p, }, message_create_params.MessageCreateParamsStreaming if stream else message_create_params.MessageCreateParamsNonStreaming, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaMessage, stream=stream or False, stream_cls=AsyncStream[BetaRawMessageStreamEvent], ) async def parse( self, *, max_tokens: int, messages: Iterable[BetaMessageParam], model: ModelParam, cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, container: Optional[message_create_params.Container] | Omit = omit, context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, diagnostics: Optional[BetaDiagnosticsParam] | Omit = omit, fallback_credit_token: Optional[message_create_params.FallbackCreditToken] | Omit = omit, fallbacks: Optional[BetaFallbacksParam] | Omit = omit, inference_geo: Optional[str] | Omit = omit, mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, metadata: BetaMetadataParam | Omit = omit, output_config: BetaOutputConfigParam | Omit = omit, output_format: Optional[type[ResponseFormatT]] | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, speed: Optional[Literal["standard", "fast"]] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, stream: Literal[False] | Literal[True] | Omit = omit, system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, temperature: float | Omit = omit, thinking: BetaThinkingConfigParam | Omit = omit, tool_choice: BetaToolChoiceParam | Omit = omit, tools: Iterable[BetaToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ParsedBetaMessage[ResponseFormatT]: _validate_output_config_conflict(output_config, output_format) _warn_output_format_deprecated(output_format) if not stream and not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: timeout = self._client._calculate_nonstreaming_timeout( max_tokens, MODEL_NONSTREAMING_TOKENS.get(model, None) ) if model in DEPRECATED_MODELS: warnings.warn( f"The model '{model}' is deprecated and will reach end-of-life on {DEPRECATED_MODELS[model]}.\nPlease migrate to a newer model. Visit https://docs.anthropic.com/en/docs/resources/model-deprecations for more information.", DeprecationWarning, stacklevel=3, ) if model in MODELS_TO_WARN_WITH_THINKING_ENABLED and thinking and thinking["type"] == "enabled": warnings.warn( f"Using Claude with {model} and 'thinking.type=enabled' is deprecated. Use 'thinking.type=adaptive' instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking", UserWarning, stacklevel=3, ) betas = [beta for beta in betas] if is_given(betas) else [] if "structured-outputs-2025-12-15" not in betas: # Ensure structured outputs beta is included for parse method betas.append("structured-outputs-2025-12-15") extra_headers = merge_headers( _helper_header("beta.messages.parse"), strip_not_given( { "anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else NOT_GIVEN, "anthropic-user-profile-id": user_profile_id, } ), _stainless_helper_header(tools, messages), extra_headers or {}, ) if is_given(output_format) and output_format is not None: adapted_type: TypeAdapter[ResponseFormatT] = TypeAdapter(output_format) try: schema = adapted_type.json_schema() transformed_output_format = BetaJSONOutputFormatParam( schema=transform_schema(schema), type="json_schema" ) except pydantic.errors.PydanticSchemaGenerationError as e: raise TypeError( ( "Could not generate JSON schema for the given `output_format` type. " "Use a type that works with `pydantic.TypeAdapter`" ) ) from e merged_output_config = _merge_output_configs(output_config, transformed_output_format) else: merged_output_config = output_config def parser(response: BetaMessage) -> ParsedBetaMessage[ResponseFormatT]: return parse_beta_response( response=response, output_format=cast( ResponseFormatT, output_format if is_given(output_format) and output_format is not None else NOT_GIVEN, ), ) return await self._post( "/v1/messages?beta=true", body=maybe_transform( { "max_tokens": max_tokens, "messages": messages, "model": model, "cache_control": cache_control, "container": container, "context_management": context_management, "diagnostics": diagnostics, "fallback_credit_token": fallback_credit_token, "fallbacks": fallbacks, "inference_geo": inference_geo, "mcp_servers": mcp_servers, "output_config": merged_output_config, "metadata": metadata, "output_format": omit, "service_tier": service_tier, "speed": speed, "stop_sequences": stop_sequences, "stream": stream, "system": system, "temperature": temperature, "thinking": thinking, "tool_choice": tool_choice, "tools": tools, "top_k": top_k, "top_p": top_p, }, message_create_params.MessageCreateParamsNonStreaming, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, post_parser=parser, ), cast_to=cast(Type[ParsedBetaMessage[ResponseFormatT]], BetaMessage), stream=False, ) @overload def tool_runner( self, *, max_tokens: int, messages: Iterable[BetaMessageParam], model: ModelParam, tools: Iterable[BetaAsyncRunnableTool | BetaToolUnionParam], cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, compaction_control: CompactionControl | Omit = omit, max_iterations: int | Omit = omit, container: Optional[message_create_params.Container] | Omit = omit, context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, diagnostics: Optional[BetaDiagnosticsParam] | Omit = omit, fallback_credit_token: Optional[message_create_params.FallbackCreditToken] | Omit = omit, fallbacks: Optional[BetaFallbacksParam] | Omit = omit, inference_geo: Optional[str] | Omit = omit, mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, metadata: BetaMetadataParam | Omit = omit, output_config: BetaOutputConfigParam | Omit = omit, output_format: Optional[type[ResponseFormatT]] | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, speed: Optional[Literal["standard", "fast"]] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, stream: Literal[False] | Omit = omit, system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, temperature: float | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, thinking: BetaThinkingConfigParam | Omit = omit, tool_choice: BetaToolChoiceParam | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> BetaAsyncToolRunner[ResponseFormatT]: ... @overload def tool_runner( self, *, max_tokens: int, messages: Iterable[BetaMessageParam], model: ModelParam, tools: Iterable[BetaAsyncRunnableTool | BetaToolUnionParam], compaction_control: CompactionControl | Omit = omit, stream: Literal[True], max_iterations: int | Omit = omit, cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, container: Optional[message_create_params.Container] | Omit = omit, context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, diagnostics: Optional[BetaDiagnosticsParam] | Omit = omit, fallback_credit_token: Optional[message_create_params.FallbackCreditToken] | Omit = omit, fallbacks: Optional[BetaFallbacksParam] | Omit = omit, inference_geo: Optional[str] | Omit = omit, mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, metadata: BetaMetadataParam | Omit = omit, output_config: BetaOutputConfigParam | Omit = omit, output_format: Optional[type[ResponseFormatT]] | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, speed: Optional[Literal["standard", "fast"]] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, temperature: float | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, thinking: BetaThinkingConfigParam | Omit = omit, tool_choice: BetaToolChoiceParam | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> BetaAsyncStreamingToolRunner[ResponseFormatT]: ... @overload def tool_runner( self, *, max_tokens: int, messages: Iterable[BetaMessageParam], model: ModelParam, tools: Iterable[BetaAsyncRunnableTool | BetaToolUnionParam], compaction_control: CompactionControl | Omit = omit, stream: bool, max_iterations: int | Omit = omit, cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, container: Optional[message_create_params.Container] | Omit = omit, context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, diagnostics: Optional[BetaDiagnosticsParam] | Omit = omit, fallback_credit_token: Optional[message_create_params.FallbackCreditToken] | Omit = omit, fallbacks: Optional[BetaFallbacksParam] | Omit = omit, inference_geo: Optional[str] | Omit = omit, mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, metadata: BetaMetadataParam | Omit = omit, output_config: BetaOutputConfigParam | Omit = omit, output_format: Optional[type[ResponseFormatT]] | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, speed: Optional[Literal["standard", "fast"]] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, temperature: float | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, thinking: BetaThinkingConfigParam | Omit = omit, tool_choice: BetaToolChoiceParam | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> BetaAsyncStreamingToolRunner[ResponseFormatT] | BetaAsyncToolRunner[ResponseFormatT]: ... def tool_runner( self, *, max_tokens: int, messages: Iterable[BetaMessageParam], model: ModelParam, tools: Iterable[BetaAsyncRunnableTool | BetaToolUnionParam], compaction_control: CompactionControl | Omit = omit, max_iterations: int | Omit = omit, cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, container: Optional[message_create_params.Container] | Omit = omit, context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, diagnostics: Optional[BetaDiagnosticsParam] | Omit = omit, fallback_credit_token: Optional[message_create_params.FallbackCreditToken] | Omit = omit, fallbacks: Optional[BetaFallbacksParam] | Omit = omit, inference_geo: Optional[str] | Omit = omit, mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, metadata: BetaMetadataParam | Omit = omit, output_config: BetaOutputConfigParam | Omit = omit, output_format: Optional[type[ResponseFormatT]] | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, speed: Optional[Literal["standard", "fast"]] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, stream: Literal[True] | Literal[False] | Omit = omit, system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, temperature: float | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, thinking: BetaThinkingConfigParam | Omit = omit, tool_choice: BetaToolChoiceParam | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> BetaAsyncToolRunner[ResponseFormatT] | BetaAsyncStreamingToolRunner[ResponseFormatT]: """Create a Message stream""" _validate_output_config_conflict(output_config, output_format) _warn_output_format_deprecated(output_format) if model in DEPRECATED_MODELS: warnings.warn( f"The model '{model}' is deprecated and will reach end-of-life on {DEPRECATED_MODELS[model]}.\nPlease migrate to a newer model. Visit https://docs.anthropic.com/en/docs/resources/model-deprecations for more information.", DeprecationWarning, stacklevel=3, ) if model in MODELS_TO_WARN_WITH_THINKING_ENABLED and thinking and thinking["type"] == "enabled": warnings.warn( f"Using Claude with {model} and 'thinking.type=enabled' is deprecated. Use 'thinking.type=adaptive' instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking", UserWarning, stacklevel=3, ) extra_headers = merge_headers( _helper_header("BetaToolRunner"), strip_not_given( { "anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else NOT_GIVEN, "anthropic-user-profile-id": user_profile_id, } ), _stainless_helper_header(tools, messages), extra_headers or {}, ) runnable_tools: list[BetaAsyncRunnableTool] = [] raw_tools: list[BetaToolUnionParam] = [] for tool in tools: if isinstance(tool, (BetaAsyncFunctionTool, BetaAsyncBuiltinFunctionTool)): runnable_tools.append(tool) else: raw_tools.append(tool) params = cast( message_create_params.ParseMessageCreateParamsBase[ResponseFormatT], { "max_tokens": max_tokens, "messages": messages, "model": model, "cache_control": cache_control, "container": container, "context_management": context_management, "diagnostics": diagnostics, "fallback_credit_token": fallback_credit_token, "fallbacks": fallbacks, "inference_geo": inference_geo, "mcp_servers": mcp_servers, "metadata": metadata, "output_config": output_config, "output_format": output_format, "service_tier": service_tier, "speed": speed, "stop_sequences": stop_sequences, "system": system, "temperature": temperature, "thinking": thinking, "tool_choice": tool_choice, "tools": [*[tool.to_dict() for tool in runnable_tools], *raw_tools], "top_k": top_k, "top_p": top_p, }, ) if stream: return BetaAsyncStreamingToolRunner[ResponseFormatT]( tools=runnable_tools, params=params, options={ "extra_headers": extra_headers, "extra_query": extra_query, "extra_body": extra_body, "timeout": timeout, }, client=cast("AsyncAnthropic", self._client), max_iterations=max_iterations if is_given(max_iterations) else None, compaction_control=compaction_control if is_given(compaction_control) else None, ) return BetaAsyncToolRunner[ResponseFormatT]( tools=runnable_tools, params=params, options={ "extra_headers": extra_headers, "extra_query": extra_query, "extra_body": extra_body, "timeout": timeout, }, client=cast("AsyncAnthropic", self._client), max_iterations=max_iterations if is_given(max_iterations) else None, compaction_control=compaction_control if is_given(compaction_control) else None, ) def stream( self, *, max_tokens: int, messages: Iterable[BetaMessageParam], model: ModelParam, cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, metadata: BetaMetadataParam | Omit = omit, output_config: BetaOutputConfigParam | Omit = omit, output_format: None | type[ResponseFormatT] | BetaJSONOutputFormatParam | Omit = omit, container: Optional[message_create_params.Container] | Omit = omit, context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, diagnostics: Optional[BetaDiagnosticsParam] | Omit = omit, fallback_credit_token: Optional[message_create_params.FallbackCreditToken] | Omit = omit, fallbacks: Optional[BetaFallbacksParam] | Omit = omit, inference_geo: Optional[str] | Omit = omit, mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, speed: Optional[Literal["standard", "fast"]] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, temperature: float | Omit = omit, thinking: BetaThinkingConfigParam | Omit = omit, tool_choice: BetaToolChoiceParam | Omit = omit, tools: Iterable[BetaToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> BetaAsyncMessageStreamManager[ResponseFormatT]: _validate_output_config_conflict(output_config, output_format) _warn_output_format_deprecated(output_format) if model in DEPRECATED_MODELS: warnings.warn( f"The model '{model}' is deprecated and will reach end-of-life on {DEPRECATED_MODELS[model]}.\nPlease migrate to a newer model. Visit https://docs.anthropic.com/en/docs/resources/model-deprecations for more information.", DeprecationWarning, stacklevel=3, ) if model in MODELS_TO_WARN_WITH_THINKING_ENABLED and thinking and thinking["type"] == "enabled": warnings.warn( f"Using Claude with {model} and 'thinking.type=enabled' is deprecated. Use 'thinking.type=adaptive' instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking", UserWarning, stacklevel=3, ) extra_headers = merge_headers( { _STAINLESS_HELPER_METHOD_HEADER: _HELPER_METHOD_STREAM, _STAINLESS_STREAM_HELPER_HEADER: "beta.messages", }, strip_not_given( { "anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else NOT_GIVEN, "anthropic-user-profile-id": user_profile_id, } ), _stainless_helper_header(tools, messages), extra_headers or {}, ) transformed_output_format: BetaJSONOutputFormatParam | Omit = omit if is_dict(output_format): transformed_output_format = cast(BetaJSONOutputFormatParam, output_format) elif is_given(output_format) and output_format is not None: adapted_type: TypeAdapter[ResponseFormatT] = TypeAdapter(output_format) try: schema = adapted_type.json_schema() transformed_output_format = BetaJSONOutputFormatParam( schema=transform_schema(schema), type="json_schema" ) except pydantic.errors.PydanticSchemaGenerationError as e: raise TypeError( ( "Could not generate JSON schema for the given `output_format` type. " "Use a type that works with `pydantic.TypeAdapter`" ) ) from e merged_output_config = _merge_output_configs(output_config, transformed_output_format) request = self._post( "/v1/messages?beta=true", body=maybe_transform( { "max_tokens": max_tokens, "messages": messages, "model": model, "cache_control": cache_control, "metadata": metadata, "output_config": merged_output_config, "output_format": omit, "container": container, "context_management": context_management, "diagnostics": diagnostics, "fallback_credit_token": fallback_credit_token, "fallbacks": fallbacks, "inference_geo": inference_geo, "mcp_servers": mcp_servers, "service_tier": service_tier, "speed": speed, "stop_sequences": stop_sequences, "system": system, "temperature": temperature, "thinking": thinking, "top_k": top_k, "top_p": top_p, "tools": tools, "tool_choice": tool_choice, "stream": True, }, message_create_params.MessageCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaMessage, stream=True, stream_cls=AsyncStream[BetaRawMessageStreamEvent], ) return BetaAsyncMessageStreamManager( request, output_format=NOT_GIVEN if is_dict(output_format) else cast(ResponseFormatT, output_format), ) async def count_tokens( self, *, messages: Iterable[BetaMessageParam], model: ModelParam, cache_control: Optional[BetaCacheControlEphemeralParam] | Omit = omit, context_management: Optional[BetaContextManagementConfigParam] | Omit = omit, mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] | Omit = omit, output_config: BetaOutputConfigParam | Omit = omit, output_format: Optional[BetaJSONOutputFormatParam] | Omit = omit, speed: Optional[Literal["standard", "fast"]] | Omit = omit, system: Union[str, Iterable[BetaTextBlockParam]] | Omit = omit, thinking: BetaThinkingConfigParam | Omit = omit, tool_choice: BetaToolChoiceParam | Omit = omit, tools: Iterable[message_count_tokens_params.Tool] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaMessageTokensCount: """ Count the number of tokens in a Message. The Token Count API can be used to count the number of tokens in a Message, including tools, images, and documents, without creating it. Learn more about token counting in our [user guide](https://platform.claude.com/docs/en/build-with-claude/token-counting) Args: messages: Input messages. Our models are trained to operate on alternating `user` and `assistant` conversational turns. When creating a new `Message`, you specify the prior conversational turns with the `messages` parameter, and the model then generates the next `Message` in the conversation. Consecutive `user` or `assistant` turns in your request will be combined into a single turn. Each input message must be an object with a `role` and `content`. You can specify a single `user`-role message, or you can include multiple `user` and `assistant` messages. If the final message uses the `assistant` role, the response content will continue immediately from the content in that message. This can be used to constrain part of the model's response. Example with a single `user` message: ```json [{ "role": "user", "content": "Hello, Claude" }] ``` Example with multiple conversational turns: ```json [ { "role": "user", "content": "Hello there." }, { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, { "role": "user", "content": "Can you explain LLMs in plain English?" } ] ``` Example with a partially-filled response from Claude: ```json [ { "role": "user", "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" }, { "role": "assistant", "content": "The best answer is (" } ] ``` Each input message `content` may be either a single `string` or an array of content blocks, where each block has a specific `type`. Using a `string` for `content` is shorthand for an array of one content block of type `"text"`. The following input messages are equivalent: ```json { "role": "user", "content": "Hello, Claude" } ``` ```json { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } ``` See [input examples](https://platform.claude.com/docs/en/build-with-claude/working-with-messages). Note that if you want to include a [system prompt](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role), you can use the top-level `system` parameter — there is no `"system"` role for input messages in the Messages API. There is a limit of 100,000 messages in a single request. model: The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. cache_control: Top-level cache control automatically applies a cache_control marker to the last cacheable block in the request. context_management: Context management configuration. This allows you to control how Claude manages context across multiple requests, such as whether to clear function results or not. mcp_servers: MCP servers to be utilized in this request output_config: Configuration options for the model's output, such as the output format. output_format: Deprecated: Use `output_config.format` instead. See [structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs) A schema to specify Claude's output format in responses. This parameter will be removed in a future release. speed: Inference speed mode. `fast` provides significantly faster output token generation at premium pricing. Not all models support `fast`; invalid combinations are rejected at create time. system: System prompt. A system prompt is a way of providing context and instructions to Claude, such as specifying a particular goal or role. See our [guide to system prompts](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role). thinking: Configuration for enabling Claude's extended thinking. When enabled, responses include `thinking` content blocks showing Claude's thinking process before the final answer. Requires a minimum budget of 1,024 tokens and counts towards your `max_tokens` limit. See [extended thinking](https://platform.claude.com/docs/en/build-with-claude/extended-thinking) for details. tool_choice: How the model should use the provided tools. The model can use a specific tool, any available tool, decide by itself, or not use tools at all. tools: Definitions of tools that the model may use. If you include `tools` in your API request, the model may return `tool_use` content blocks that represent the model's use of those tools. You can then run those tools using the tool input generated by the model and then optionally return results back to the model using `tool_result` content blocks. There are two types of tools: **client tools** and **server tools**. The behavior described below applies to client tools. For [server tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools), see their individual documentation as each has its own behavior (e.g., the [web search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool)). Each tool definition includes: - `name`: Name of the tool. - `description`: Optional, but strongly-recommended description of the tool. - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the tool `input` shape that the model will produce in `tool_use` output content blocks. For example, if you defined `tools` as: ```json [ { "name": "get_stock_price", "description": "Get the current stock price for a given ticker symbol.", "input_schema": { "type": "object", "properties": { "ticker": { "type": "string", "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." } }, "required": ["ticker"] } } ] ``` And then asked the model "What's the S&P 500 at today?", the model might produce `tool_use` content blocks in the response like this: ```json [ { "type": "tool_use", "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "name": "get_stock_price", "input": { "ticker": "^GSPC" } } ] ``` You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an input, and return the following back to the model in a subsequent `user` message: ```json [ { "type": "tool_result", "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "content": "259.75 USD" } ] ``` Tools can be used for workflows that include running client-side tools and functions, or more generally whenever you want the model to produce a particular JSON structure of output. See our [guide](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview) for more details. betas: Optional header to specify the beta version(s) you want to use. user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a party other than your organization. Requires the `user-profiles` beta header. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ _validate_output_config_conflict(output_config, output_format) _warn_output_format_deprecated(output_format) merged_output_config = _merge_output_configs(output_config, output_format) extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["token-counting-2024-11-01"])) if is_given(betas) else not_given, "anthropic-user-profile-id": user_profile_id, } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "token-counting-2024-11-01", **(extra_headers or {})} return await self._post( "/v1/messages/count_tokens?beta=true", body=await async_maybe_transform( { "messages": messages, "model": model, "cache_control": cache_control, "context_management": context_management, "mcp_servers": mcp_servers, "mcp_servers": mcp_servers, "output_config": merged_output_config, "output_format": omit, "speed": speed, "system": system, "thinking": thinking, "tool_choice": tool_choice, "tools": tools, }, message_count_tokens_params.MessageCountTokensParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaMessageTokensCount, ) class MessagesWithRawResponse: def __init__(self, messages: Messages) -> None: self._messages = messages self.create = _legacy_response.to_raw_response_wrapper( messages.create, ) self.parse = _legacy_response.to_raw_response_wrapper( messages.parse, ) self.count_tokens = _legacy_response.to_raw_response_wrapper( messages.count_tokens, ) @cached_property def batches(self) -> BatchesWithRawResponse: return BatchesWithRawResponse(self._messages.batches) class AsyncMessagesWithRawResponse: def __init__(self, messages: AsyncMessages) -> None: self._messages = messages self.create = _legacy_response.async_to_raw_response_wrapper( messages.create, ) self.parse = _legacy_response.async_to_raw_response_wrapper( messages.parse, ) self.count_tokens = _legacy_response.async_to_raw_response_wrapper( messages.count_tokens, ) @cached_property def batches(self) -> AsyncBatchesWithRawResponse: return AsyncBatchesWithRawResponse(self._messages.batches) class MessagesWithStreamingResponse: def __init__(self, messages: Messages) -> None: self._messages = messages self.create = to_streamed_response_wrapper( messages.create, ) self.count_tokens = to_streamed_response_wrapper( messages.count_tokens, ) @cached_property def batches(self) -> BatchesWithStreamingResponse: return BatchesWithStreamingResponse(self._messages.batches) class AsyncMessagesWithStreamingResponse: def __init__(self, messages: AsyncMessages) -> None: self._messages = messages self.create = async_to_streamed_response_wrapper( messages.create, ) self.count_tokens = async_to_streamed_response_wrapper( messages.count_tokens, ) @cached_property def batches(self) -> AsyncBatchesWithStreamingResponse: return AsyncBatchesWithStreamingResponse(self._messages.batches) def validate_output_format(output_format: object) -> None: if inspect.isclass(output_format) and issubclass(output_format, pydantic.BaseModel): raise TypeError( "You tried to pass a `BaseModel` class to `beta.messages.create()`; You must use `beta.messages.parse()` instead" ) def _validate_output_config_conflict( output_config: BetaOutputConfigParam | Omit, output_format: object, ) -> None: if is_given(output_format) and output_format is not None and is_given(output_config): if "format" in output_config and output_config["format"] is not None: raise AnthropicError( "Both output_format and output_config.format were provided. " "Please use only output_config.format (output_format is deprecated).", ) def _merge_output_configs( output_config: BetaOutputConfigParam | Omit, output_format: Optional[BetaJSONOutputFormatParam] | Omit, ) -> BetaOutputConfigParam | Omit: if is_given(output_format): if is_given(output_config): return {**output_config, "format": output_format} else: return {"format": output_format} return output_config def _warn_output_format_deprecated(output_format: object) -> None: """Emit deprecation warning if output_format is provided.""" if is_given(output_format) and output_format is not None: warnings.warn( "The 'output_format' parameter is deprecated. Please use 'output_config.format' instead.", DeprecationWarning, stacklevel=4, ) anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/models.py000066400000000000000000000304651523216435200253140ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List import httpx from ... import _legacy_response from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ..._utils import is_given, path_template, maybe_transform, strip_not_given from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ...pagination import SyncPage, AsyncPage from ...types.beta import model_list_params from ..._base_client import AsyncPaginator, make_request_options from ...types.anthropic_beta_param import AnthropicBetaParam from ...types.beta.beta_model_info import BetaModelInfo __all__ = ["Models", "AsyncModels"] class Models(SyncAPIResource): @cached_property def with_raw_response(self) -> ModelsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return ModelsWithRawResponse(self) @cached_property def with_streaming_response(self) -> ModelsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return ModelsWithStreamingResponse(self) def retrieve( self, model_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaModelInfo: """ Get a specific model. The Models API response can be used to determine information about a specific model or resolve a model alias to a model ID. Args: model_id: Model identifier or alias. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not model_id: raise ValueError(f"Expected a non-empty value for `model_id` but received {model_id!r}") extra_headers = { **strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), **(extra_headers or {}), } return self._get( path_template("/v1/models/{model_id}?beta=true", model_id=model_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaModelInfo, ) def list( self, *, after_id: str | Omit = omit, before_id: str | Omit = omit, limit: int | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncPage[BetaModelInfo]: """ List available models. The Models API response can be used to determine which models are available for use in the API. More recently released models are listed first. Args: after_id: ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately after this object. before_id: ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately before this object. limit: Number of items to return per page. Defaults to `20`. Ranges from `1` to `1000`. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), **(extra_headers or {}), } return self._get_api_list( "/v1/models?beta=true", page=SyncPage[BetaModelInfo], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "after_id": after_id, "before_id": before_id, "limit": limit, }, model_list_params.ModelListParams, ), ), model=BetaModelInfo, ) class AsyncModels(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncModelsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncModelsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncModelsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncModelsWithStreamingResponse(self) async def retrieve( self, model_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaModelInfo: """ Get a specific model. The Models API response can be used to determine information about a specific model or resolve a model alias to a model ID. Args: model_id: Model identifier or alias. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not model_id: raise ValueError(f"Expected a non-empty value for `model_id` but received {model_id!r}") extra_headers = { **strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), **(extra_headers or {}), } return await self._get( path_template("/v1/models/{model_id}?beta=true", model_id=model_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaModelInfo, ) def list( self, *, after_id: str | Omit = omit, before_id: str | Omit = omit, limit: int | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[BetaModelInfo, AsyncPage[BetaModelInfo]]: """ List available models. The Models API response can be used to determine which models are available for use in the API. More recently released models are listed first. Args: after_id: ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately after this object. before_id: ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately before this object. limit: Number of items to return per page. Defaults to `20`. Ranges from `1` to `1000`. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), **(extra_headers or {}), } return self._get_api_list( "/v1/models?beta=true", page=AsyncPage[BetaModelInfo], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "after_id": after_id, "before_id": before_id, "limit": limit, }, model_list_params.ModelListParams, ), ), model=BetaModelInfo, ) class ModelsWithRawResponse: def __init__(self, models: Models) -> None: self._models = models self.retrieve = _legacy_response.to_raw_response_wrapper( models.retrieve, ) self.list = _legacy_response.to_raw_response_wrapper( models.list, ) class AsyncModelsWithRawResponse: def __init__(self, models: AsyncModels) -> None: self._models = models self.retrieve = _legacy_response.async_to_raw_response_wrapper( models.retrieve, ) self.list = _legacy_response.async_to_raw_response_wrapper( models.list, ) class ModelsWithStreamingResponse: def __init__(self, models: Models) -> None: self._models = models self.retrieve = to_streamed_response_wrapper( models.retrieve, ) self.list = to_streamed_response_wrapper( models.list, ) class AsyncModelsWithStreamingResponse: def __init__(self, models: AsyncModels) -> None: self._models = models self.retrieve = async_to_streamed_response_wrapper( models.retrieve, ) self.list = async_to_streamed_response_wrapper( models.list, ) anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/sessions/000077500000000000000000000000001523216435200253155ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/sessions/__init__.py000066400000000000000000000030741523216435200274320ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .events import ( Events, AsyncEvents, EventsWithRawResponse, AsyncEventsWithRawResponse, EventsWithStreamingResponse, AsyncEventsWithStreamingResponse, ) from .threads import ( Threads, AsyncThreads, ThreadsWithRawResponse, AsyncThreadsWithRawResponse, ThreadsWithStreamingResponse, AsyncThreadsWithStreamingResponse, ) from .sessions import ( Sessions, AsyncSessions, SessionsWithRawResponse, AsyncSessionsWithRawResponse, SessionsWithStreamingResponse, AsyncSessionsWithStreamingResponse, ) from .resources import ( Resources, AsyncResources, ResourcesWithRawResponse, AsyncResourcesWithRawResponse, ResourcesWithStreamingResponse, AsyncResourcesWithStreamingResponse, ) __all__ = [ "Events", "AsyncEvents", "EventsWithRawResponse", "AsyncEventsWithRawResponse", "EventsWithStreamingResponse", "AsyncEventsWithStreamingResponse", "Resources", "AsyncResources", "ResourcesWithRawResponse", "AsyncResourcesWithRawResponse", "ResourcesWithStreamingResponse", "AsyncResourcesWithStreamingResponse", "Threads", "AsyncThreads", "ThreadsWithRawResponse", "AsyncThreadsWithRawResponse", "ThreadsWithStreamingResponse", "AsyncThreadsWithStreamingResponse", "Sessions", "AsyncSessions", "SessionsWithRawResponse", "AsyncSessionsWithRawResponse", "SessionsWithStreamingResponse", "AsyncSessionsWithStreamingResponse", ] anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/sessions/events.py000066400000000000000000000712661523216435200272070ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import TYPE_CHECKING, Any, List, Union, Iterable, cast from datetime import datetime from itertools import chain from typing_extensions import Literal import httpx if TYPE_CHECKING: from collections.abc import Sequence from ...._client import AsyncAnthropic from ....lib.tools._beta_session_runner import SessionToolRunner, BetaAnyRunnableTool from .... import _legacy_response from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given from ...._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ...._streaming import Stream, AsyncStream from ....pagination import SyncPageCursor, AsyncPageCursor from ...._base_client import AsyncPaginator, make_request_options from ....types.beta.sessions import event_list_params, event_send_params, event_stream_params from ....types.anthropic_beta_param import AnthropicBetaParam from ....types.beta.beta_managed_agents_delta_type import BetaManagedAgentsDeltaType from ....types.beta.sessions.beta_managed_agents_event_params import BetaManagedAgentsEventParams from ....types.beta.sessions.beta_managed_agents_session_event import BetaManagedAgentsSessionEvent from ....types.beta.sessions.beta_managed_agents_send_session_events import BetaManagedAgentsSendSessionEvents from ....types.beta.sessions.beta_managed_agents_stream_session_events import BetaManagedAgentsStreamSessionEvents __all__ = ["Events", "AsyncEvents"] class Events(SyncAPIResource): @cached_property def with_raw_response(self) -> EventsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return EventsWithRawResponse(self) @cached_property def with_streaming_response(self) -> EventsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return EventsWithStreamingResponse(self) def list( self, session_id: str, *, created_at_gt: Union[str, datetime] | Omit = omit, created_at_gte: Union[str, datetime] | Omit = omit, created_at_lt: Union[str, datetime] | Omit = omit, created_at_lte: Union[str, datetime] | Omit = omit, limit: int | Omit = omit, order: Literal["asc", "desc"] | Omit = omit, page: str | Omit = omit, types: SequenceNotStr[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncPageCursor[BetaManagedAgentsSessionEvent]: """ List Events Args: created_at_gt: Return events created after this time (exclusive). Compared against the event's `processed_at` value. created_at_gte: Return events created at or after this time (inclusive). Compared against the event's `processed_at` value. created_at_lt: Return events created before this time (exclusive). Compared against the event's `processed_at` value. created_at_lte: Return events created at or before this time (inclusive). Compared against the event's `processed_at` value. limit: Query parameter for limit order: Sort direction for results, ordered by the event's `processed_at`. Defaults to asc (chronological). page: Opaque pagination cursor from a previous response's next_page. types: Filter by event type. Values match the `type` field on returned events (for example, `user.message` or `agent.tool_use`). Omit to return all event types. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get_api_list( path_template("/v1/sessions/{session_id}/events?beta=true", session_id=session_id), page=SyncPageCursor[BetaManagedAgentsSessionEvent], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "created_at_gt": created_at_gt, "created_at_gte": created_at_gte, "created_at_lt": created_at_lt, "created_at_lte": created_at_lte, "limit": limit, "order": order, "page": page, "types": types, }, event_list_params.EventListParams, ), ), model=cast( Any, BetaManagedAgentsSessionEvent ), # Union types cannot be passed in as arguments in the type system ) def send( self, session_id: str, *, events: Iterable[BetaManagedAgentsEventParams], betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsSendSessionEvents: """ Send Events Args: events: Events to send to the `session`. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._post( path_template("/v1/sessions/{session_id}/events?beta=true", session_id=session_id), body=maybe_transform({"events": events}, event_send_params.EventSendParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsSendSessionEvents, ) def stream( self, session_id: str, *, event_deltas: List[BetaManagedAgentsDeltaType] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Stream[BetaManagedAgentsStreamSessionEvents]: """ Stream Events Args: event_deltas: When set, this connection also receives streaming deltas (`event_start`, `event_delta`) while an event is being produced, before the event itself arrives. Deltas are best-effort; when the final event is produced it carries the complete content. A model request that ends early (an error or interrupt) produces no final event — its terminal `span.model_request_end` closes the preview. Accepts one or more event types to preview and may be repeated: `agent.message` streams `content_delta` fragments; `agent.thinking` is start-only — a signal that the agent has begun extended thinking, concluded by the `agent.thinking` event itself. Only previews of the requested event types are sent. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get( path_template("/v1/sessions/{session_id}/events/stream?beta=true", session_id=session_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform({"event_deltas": event_deltas}, event_stream_params.EventStreamParams), ), cast_to=cast( Any, BetaManagedAgentsStreamSessionEvents ), # Union types cannot be passed in as arguments in the type system stream=True, stream_cls=Stream[BetaManagedAgentsStreamSessionEvents], ) class AsyncEvents(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncEventsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncEventsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncEventsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncEventsWithStreamingResponse(self) def list( self, session_id: str, *, created_at_gt: Union[str, datetime] | Omit = omit, created_at_gte: Union[str, datetime] | Omit = omit, created_at_lt: Union[str, datetime] | Omit = omit, created_at_lte: Union[str, datetime] | Omit = omit, limit: int | Omit = omit, order: Literal["asc", "desc"] | Omit = omit, page: str | Omit = omit, types: SequenceNotStr[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[BetaManagedAgentsSessionEvent, AsyncPageCursor[BetaManagedAgentsSessionEvent]]: """ List Events Args: created_at_gt: Return events created after this time (exclusive). Compared against the event's `processed_at` value. created_at_gte: Return events created at or after this time (inclusive). Compared against the event's `processed_at` value. created_at_lt: Return events created before this time (exclusive). Compared against the event's `processed_at` value. created_at_lte: Return events created at or before this time (inclusive). Compared against the event's `processed_at` value. limit: Query parameter for limit order: Sort direction for results, ordered by the event's `processed_at`. Defaults to asc (chronological). page: Opaque pagination cursor from a previous response's next_page. types: Filter by event type. Values match the `type` field on returned events (for example, `user.message` or `agent.tool_use`). Omit to return all event types. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get_api_list( path_template("/v1/sessions/{session_id}/events?beta=true", session_id=session_id), page=AsyncPageCursor[BetaManagedAgentsSessionEvent], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "created_at_gt": created_at_gt, "created_at_gte": created_at_gte, "created_at_lt": created_at_lt, "created_at_lte": created_at_lte, "limit": limit, "order": order, "page": page, "types": types, }, event_list_params.EventListParams, ), ), model=cast( Any, BetaManagedAgentsSessionEvent ), # Union types cannot be passed in as arguments in the type system ) async def send( self, session_id: str, *, events: Iterable[BetaManagedAgentsEventParams], betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsSendSessionEvents: """ Send Events Args: events: Events to send to the `session`. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._post( path_template("/v1/sessions/{session_id}/events?beta=true", session_id=session_id), body=await async_maybe_transform({"events": events}, event_send_params.EventSendParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsSendSessionEvents, ) async def stream( self, session_id: str, *, event_deltas: List[BetaManagedAgentsDeltaType] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncStream[BetaManagedAgentsStreamSessionEvents]: """ Stream Events Args: event_deltas: When set, this connection also receives streaming deltas (`event_start`, `event_delta`) while an event is being produced, before the event itself arrives. Deltas are best-effort; when the final event is produced it carries the complete content. A model request that ends early (an error or interrupt) produces no final event — its terminal `span.model_request_end` closes the preview. Accepts one or more event types to preview and may be repeated: `agent.message` streams `content_delta` fragments; `agent.thinking` is start-only — a signal that the agent has begun extended thinking, concluded by the `agent.thinking` event itself. Only previews of the requested event types are sent. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._get( path_template("/v1/sessions/{session_id}/events/stream?beta=true", session_id=session_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=await async_maybe_transform( {"event_deltas": event_deltas}, event_stream_params.EventStreamParams ), ), cast_to=cast( Any, BetaManagedAgentsStreamSessionEvents ), # Union types cannot be passed in as arguments in the type system stream=True, stream_cls=AsyncStream[BetaManagedAgentsStreamSessionEvents], ) def tool_runner( self, session_id: str, *, tools: Sequence[BetaAnyRunnableTool], max_idle: float | None | NotGiven = not_given, environment_key: str | None = None, extra_headers: Headers | None = None, ) -> SessionToolRunner: """Dispatch a self-hosted session's tool-call events. The sessions-side counterpart to ``client.beta.messages.tool_runner``: returns a :class:`~anthropic.lib.environments.SessionToolRunner` — an async iterable that attaches to the session's event stream, reconciles against the events-list endpoint, runs the matching tool from ``tools`` for each tool-call event, posts the matching result event back, and yields one :class:`~anthropic.lib.environments.DispatchedToolCall` per completed call. It handles both tool-call kinds: ``agent.tool_use`` (built-in agent-toolset tools) answered with ``user.tool_result``, and ``agent.custom_tool_use`` (custom, user-defined tools) answered with ``user.custom_tool_result``. A call the server gated behind user confirmation (``evaluated_permission`` ``ask``, e.g. a tool configured with the ``always_ask`` permission policy) is held until the matching ``user.tool_confirmation`` event arrives — executed on ``allow``, never executed on ``deny`` (the denied call is still yielded with ``confirmation="deny"`` so it can be observed). Internally drives event-stream reconnect (with capped backoff) via an anyio task group so it works under both ``asyncio`` and ``trio``. Iteration ends when the session terminates (``session.status_terminated`` / ``session.deleted``), when the consumer breaks out of the loop, or — once the session has gone idle with ``stop_reason`` ``end_turn`` — ``max_idle`` seconds elapse with no new event (any new event resets that countdown; it re-arms on the next ``end_turn`` idle). ``max_idle=None`` disables that last condition. It does **not** touch the work-item lease — wrap it in an :class:`~anthropic.lib.environments.EnvironmentWorker` if you need heartbeating / force-stop. Usage:: from anthropic.lib.tools.agent_toolset import AgentToolContext, beta_agent_toolset_20260401 async with AgentToolContext(workdir=...) as env: async for call in client.beta.sessions.events.tool_runner( work.data.id, tools=[*beta_agent_toolset_20260401(env), my_tool], ): ... Args: session_id: The session whose events stream we attach to. Passed positionally, matching ``list`` / ``send`` / ``stream`` on this resource. tools: Registry of tool callables the runner will execute when the agent emits matching ``agent.tool_use`` / ``agent.custom_tool_use`` events — the same :class:`~anthropic.lib.tools.BetaAsyncFunctionTool` shape ``client.beta.messages.tool_runner`` accepts. max_idle: Seconds to keep running after the session goes idle with ``stop_reason`` ``end_turn`` before stopping; any new event resets the countdown. Defaults to ``DEFAULT_MAX_IDLE`` (60s) when not given. ``None`` disables it. environment_key: The self-hosted environment key. When set, the runner builds a Bearer-only scoped sub-client keyed to that environment for the event stream / list / send calls; leave it unset to authenticate those calls with the parent client's own credentials. extra_headers: Optional headers passed through per request on every call the runner makes (event stream / list / send). They are threaded into each call's ``extra_headers=`` and never assigned onto the client, so client state is not mutated. Auth and ``x-stainless-helper`` are supplied by the runner's scoped sub-client (and the parent client's ``default_headers`` propagate via its ``client.copy()``); a header given here overrides the scoped client's same-named default for that request, so use it for caller passthrough (e.g. trace ids), not to set auth. """ # DEFAULT_MAX_IDLE resolved here rather than as a literal signature # default so the value can't drift from the constant; the lazy import # also keeps the host-only environment lib out of ``import anthropic``. from ....lib.tools._beta_session_runner import DEFAULT_MAX_IDLE, SessionToolRunner if not is_given(max_idle): max_idle = DEFAULT_MAX_IDLE return SessionToolRunner( cast("AsyncAnthropic", self._client), session_id, tools=tools, max_idle=max_idle, environment_key=environment_key, extra_headers=extra_headers, ) class EventsWithRawResponse: def __init__(self, events: Events) -> None: self._events = events self.list = _legacy_response.to_raw_response_wrapper( events.list, ) self.send = _legacy_response.to_raw_response_wrapper( events.send, ) self.stream = _legacy_response.to_raw_response_wrapper( events.stream, ) class AsyncEventsWithRawResponse: def __init__(self, events: AsyncEvents) -> None: self._events = events self.list = _legacy_response.async_to_raw_response_wrapper( events.list, ) self.send = _legacy_response.async_to_raw_response_wrapper( events.send, ) self.stream = _legacy_response.async_to_raw_response_wrapper( events.stream, ) class EventsWithStreamingResponse: def __init__(self, events: Events) -> None: self._events = events self.list = to_streamed_response_wrapper( events.list, ) self.send = to_streamed_response_wrapper( events.send, ) self.stream = to_streamed_response_wrapper( events.stream, ) class AsyncEventsWithStreamingResponse: def __init__(self, events: AsyncEvents) -> None: self._events = events self.list = async_to_streamed_response_wrapper( events.list, ) self.send = async_to_streamed_response_wrapper( events.send, ) self.stream = async_to_streamed_response_wrapper( events.stream, ) anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/sessions/resources.py000066400000000000000000000744601523216435200277140ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Any, List, Optional, cast from itertools import chain from typing_extensions import Literal import httpx from .... import _legacy_response from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ...._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ....pagination import SyncPageCursor, AsyncPageCursor from ...._base_client import AsyncPaginator, make_request_options from ....types.beta.sessions import resource_add_params, resource_list_params, resource_update_params from ....types.anthropic_beta_param import AnthropicBetaParam from ....types.beta.sessions.resource_update_response import ResourceUpdateResponse from ....types.beta.sessions.resource_retrieve_response import ResourceRetrieveResponse from ....types.beta.sessions.beta_managed_agents_file_resource import BetaManagedAgentsFileResource from ....types.beta.sessions.beta_managed_agents_session_resource import BetaManagedAgentsSessionResource from ....types.beta.sessions.beta_managed_agents_delete_session_resource import BetaManagedAgentsDeleteSessionResource __all__ = ["Resources", "AsyncResources"] class Resources(SyncAPIResource): @cached_property def with_raw_response(self) -> ResourcesWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return ResourcesWithRawResponse(self) @cached_property def with_streaming_response(self) -> ResourcesWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return ResourcesWithStreamingResponse(self) def retrieve( self, resource_id: str, *, session_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ResourceRetrieveResponse: """ Get Session Resource Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") if not resource_id: raise ValueError(f"Expected a non-empty value for `resource_id` but received {resource_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return cast( ResourceRetrieveResponse, self._get( path_template( "/v1/sessions/{session_id}/resources/{resource_id}?beta=true", session_id=session_id, resource_id=resource_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=cast( Any, ResourceRetrieveResponse ), # Union types cannot be passed in as arguments in the type system ), ) def update( self, resource_id: str, *, session_id: str, authorization_token: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ResourceUpdateResponse: """ Update Session Resource Args: authorization_token: New authorization token for the resource. Currently only `github_repository` resources support token rotation. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") if not resource_id: raise ValueError(f"Expected a non-empty value for `resource_id` but received {resource_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return cast( ResourceUpdateResponse, self._post( path_template( "/v1/sessions/{session_id}/resources/{resource_id}?beta=true", session_id=session_id, resource_id=resource_id, ), body=maybe_transform( {"authorization_token": authorization_token}, resource_update_params.ResourceUpdateParams ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=cast( Any, ResourceUpdateResponse ), # Union types cannot be passed in as arguments in the type system ), ) def list( self, session_id: str, *, limit: int | Omit = omit, page: str | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncPageCursor[BetaManagedAgentsSessionResource]: """ List Session Resources Args: limit: Maximum number of resources to return per page (max 1000). If omitted, returns all resources. page: Opaque cursor from a previous response's next_page field. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get_api_list( path_template("/v1/sessions/{session_id}/resources?beta=true", session_id=session_id), page=SyncPageCursor[BetaManagedAgentsSessionResource], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "limit": limit, "page": page, }, resource_list_params.ResourceListParams, ), ), model=cast( Any, BetaManagedAgentsSessionResource ), # Union types cannot be passed in as arguments in the type system ) def delete( self, resource_id: str, *, session_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsDeleteSessionResource: """ Delete Session Resource Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") if not resource_id: raise ValueError(f"Expected a non-empty value for `resource_id` but received {resource_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._delete( path_template( "/v1/sessions/{session_id}/resources/{resource_id}?beta=true", session_id=session_id, resource_id=resource_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsDeleteSessionResource, ) def add( self, session_id: str, *, file_id: str, type: Literal["file"], mount_path: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsFileResource: """ Add Session Resource Args: file_id: ID of a previously uploaded file. mount_path: Mount path in the container. Defaults to `/mnt/session/uploads/`. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._post( path_template("/v1/sessions/{session_id}/resources?beta=true", session_id=session_id), body=maybe_transform( { "file_id": file_id, "type": type, "mount_path": mount_path, }, resource_add_params.ResourceAddParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsFileResource, ) class AsyncResources(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncResourcesWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncResourcesWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncResourcesWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncResourcesWithStreamingResponse(self) async def retrieve( self, resource_id: str, *, session_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ResourceRetrieveResponse: """ Get Session Resource Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") if not resource_id: raise ValueError(f"Expected a non-empty value for `resource_id` but received {resource_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return cast( ResourceRetrieveResponse, await self._get( path_template( "/v1/sessions/{session_id}/resources/{resource_id}?beta=true", session_id=session_id, resource_id=resource_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=cast( Any, ResourceRetrieveResponse ), # Union types cannot be passed in as arguments in the type system ), ) async def update( self, resource_id: str, *, session_id: str, authorization_token: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ResourceUpdateResponse: """ Update Session Resource Args: authorization_token: New authorization token for the resource. Currently only `github_repository` resources support token rotation. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") if not resource_id: raise ValueError(f"Expected a non-empty value for `resource_id` but received {resource_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return cast( ResourceUpdateResponse, await self._post( path_template( "/v1/sessions/{session_id}/resources/{resource_id}?beta=true", session_id=session_id, resource_id=resource_id, ), body=await async_maybe_transform( {"authorization_token": authorization_token}, resource_update_params.ResourceUpdateParams ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=cast( Any, ResourceUpdateResponse ), # Union types cannot be passed in as arguments in the type system ), ) def list( self, session_id: str, *, limit: int | Omit = omit, page: str | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[BetaManagedAgentsSessionResource, AsyncPageCursor[BetaManagedAgentsSessionResource]]: """ List Session Resources Args: limit: Maximum number of resources to return per page (max 1000). If omitted, returns all resources. page: Opaque cursor from a previous response's next_page field. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get_api_list( path_template("/v1/sessions/{session_id}/resources?beta=true", session_id=session_id), page=AsyncPageCursor[BetaManagedAgentsSessionResource], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "limit": limit, "page": page, }, resource_list_params.ResourceListParams, ), ), model=cast( Any, BetaManagedAgentsSessionResource ), # Union types cannot be passed in as arguments in the type system ) async def delete( self, resource_id: str, *, session_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsDeleteSessionResource: """ Delete Session Resource Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") if not resource_id: raise ValueError(f"Expected a non-empty value for `resource_id` but received {resource_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._delete( path_template( "/v1/sessions/{session_id}/resources/{resource_id}?beta=true", session_id=session_id, resource_id=resource_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsDeleteSessionResource, ) async def add( self, session_id: str, *, file_id: str, type: Literal["file"], mount_path: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsFileResource: """ Add Session Resource Args: file_id: ID of a previously uploaded file. mount_path: Mount path in the container. Defaults to `/mnt/session/uploads/`. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._post( path_template("/v1/sessions/{session_id}/resources?beta=true", session_id=session_id), body=await async_maybe_transform( { "file_id": file_id, "type": type, "mount_path": mount_path, }, resource_add_params.ResourceAddParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsFileResource, ) class ResourcesWithRawResponse: def __init__(self, resources: Resources) -> None: self._resources = resources self.retrieve = _legacy_response.to_raw_response_wrapper( resources.retrieve, ) self.update = _legacy_response.to_raw_response_wrapper( resources.update, ) self.list = _legacy_response.to_raw_response_wrapper( resources.list, ) self.delete = _legacy_response.to_raw_response_wrapper( resources.delete, ) self.add = _legacy_response.to_raw_response_wrapper( resources.add, ) class AsyncResourcesWithRawResponse: def __init__(self, resources: AsyncResources) -> None: self._resources = resources self.retrieve = _legacy_response.async_to_raw_response_wrapper( resources.retrieve, ) self.update = _legacy_response.async_to_raw_response_wrapper( resources.update, ) self.list = _legacy_response.async_to_raw_response_wrapper( resources.list, ) self.delete = _legacy_response.async_to_raw_response_wrapper( resources.delete, ) self.add = _legacy_response.async_to_raw_response_wrapper( resources.add, ) class ResourcesWithStreamingResponse: def __init__(self, resources: Resources) -> None: self._resources = resources self.retrieve = to_streamed_response_wrapper( resources.retrieve, ) self.update = to_streamed_response_wrapper( resources.update, ) self.list = to_streamed_response_wrapper( resources.list, ) self.delete = to_streamed_response_wrapper( resources.delete, ) self.add = to_streamed_response_wrapper( resources.add, ) class AsyncResourcesWithStreamingResponse: def __init__(self, resources: AsyncResources) -> None: self._resources = resources self.retrieve = async_to_streamed_response_wrapper( resources.retrieve, ) self.update = async_to_streamed_response_wrapper( resources.update, ) self.list = async_to_streamed_response_wrapper( resources.list, ) self.delete = async_to_streamed_response_wrapper( resources.delete, ) self.add = async_to_streamed_response_wrapper( resources.add, ) anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/sessions/sessions.py000066400000000000000000001231741523216435200275450ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Union, Iterable, Optional from datetime import datetime from itertools import chain from typing_extensions import Literal import httpx from .... import _legacy_response from .events import ( Events, AsyncEvents, EventsWithRawResponse, AsyncEventsWithRawResponse, EventsWithStreamingResponse, AsyncEventsWithStreamingResponse, ) from ...._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given from ...._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform from .resources import ( Resources, AsyncResources, ResourcesWithRawResponse, AsyncResourcesWithRawResponse, ResourcesWithStreamingResponse, AsyncResourcesWithStreamingResponse, ) from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ....pagination import SyncBidirectionalPageCursor, AsyncBidirectionalPageCursor from ....types.beta import ( session_list_params, session_create_params, session_update_params, ) from ...._base_client import AsyncPaginator, make_request_options from .threads.threads import ( Threads, AsyncThreads, ThreadsWithRawResponse, AsyncThreadsWithRawResponse, ThreadsWithStreamingResponse, AsyncThreadsWithStreamingResponse, ) from ....types.anthropic_beta_param import AnthropicBetaParam from ....types.beta.beta_managed_agents_session import BetaManagedAgentsSession from ....types.beta.beta_managed_agents_deleted_session import BetaManagedAgentsDeletedSession from ....types.beta.beta_managed_agents_session_agent_update_param import BetaManagedAgentsSessionAgentUpdateParam __all__ = ["Sessions", "AsyncSessions"] class Sessions(SyncAPIResource): @cached_property def events(self) -> Events: return Events(self._client) @cached_property def resources(self) -> Resources: return Resources(self._client) @cached_property def threads(self) -> Threads: return Threads(self._client) @cached_property def with_raw_response(self) -> SessionsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return SessionsWithRawResponse(self) @cached_property def with_streaming_response(self) -> SessionsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return SessionsWithStreamingResponse(self) def create( self, *, agent: session_create_params.Agent, environment_id: str, initial_events: Iterable[session_create_params.InitialEvent] | Omit = omit, metadata: Dict[str, str] | Omit = omit, resources: Iterable[session_create_params.Resource] | Omit = omit, title: Optional[str] | Omit = omit, vault_ids: SequenceNotStr[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsSession: """Create Session Args: agent: Agent identifier. Accepts the `agent` ID string, which pins the latest version for the session, or an `agent` object with both id and version specified. environment_id: ID of the `environment` defining the container configuration for this session. initial_events: Initial events to send to the `session` at creation, processed in order. Supports `user.message` and `user.define_outcome` events. Maximum 50 events. metadata: Arbitrary key-value metadata attached to the session. Maximum 16 pairs, keys up to 64 chars, values up to 512 chars. resources: Resources (e.g. repositories, files) to mount into the session's container. title: Human-readable session title. vault_ids: Vault IDs for stored credentials the agent can use during the session. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._post( "/v1/sessions?beta=true", body=maybe_transform( { "agent": agent, "environment_id": environment_id, "initial_events": initial_events, "metadata": metadata, "resources": resources, "title": title, "vault_ids": vault_ids, }, session_create_params.SessionCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsSession, ) def retrieve( self, session_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsSession: """ Get Session Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get( path_template("/v1/sessions/{session_id}?beta=true", session_id=session_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsSession, ) def update( self, session_id: str, *, agent: BetaManagedAgentsSessionAgentUpdateParam | Omit = omit, metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, title: Optional[str] | Omit = omit, vault_ids: SequenceNotStr[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsSession: """Update Session Args: agent: Mid-session agent configuration update. Only `tools` and `mcp_servers` are updatable. Full replacement: the provided array becomes the new value. To preserve existing entries, GET the session, modify the array, and POST it back. metadata: Metadata patch. Set a key to a string to upsert it, or to null to delete it. Omit the field to preserve. title: Human-readable session title. vault_ids: Vault IDs (`vlt_*`) to attach to the session. Not yet supported; requests setting this field are rejected. Reserved for future use. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._post( path_template("/v1/sessions/{session_id}?beta=true", session_id=session_id), body=maybe_transform( { "agent": agent, "metadata": metadata, "title": title, "vault_ids": vault_ids, }, session_update_params.SessionUpdateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsSession, ) def list( self, *, agent_id: str | Omit = omit, agent_version: int | Omit = omit, created_at_gt: Union[str, datetime] | Omit = omit, created_at_gte: Union[str, datetime] | Omit = omit, created_at_lt: Union[str, datetime] | Omit = omit, created_at_lte: Union[str, datetime] | Omit = omit, deployment_id: str | Omit = omit, include_archived: bool | Omit = omit, limit: int | Omit = omit, memory_store_id: str | Omit = omit, order: Literal["asc", "desc"] | Omit = omit, page: str | Omit = omit, statuses: List[Literal["rescheduling", "running", "idle", "terminated"]] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncBidirectionalPageCursor[BetaManagedAgentsSession]: """ List Sessions Args: agent_id: Filter sessions created with this agent ID. agent_version: Filter by agent version. Only applies when agent_id is also set. created_at_gt: Return sessions created after this time (exclusive). created_at_gte: Return sessions created at or after this time (inclusive). created_at_lt: Return sessions created before this time (exclusive). created_at_lte: Return sessions created at or before this time (inclusive). deployment_id: Filter sessions created by this deployment ID. include_archived: When true, includes archived sessions. Default: false (exclude archived). limit: Maximum number of results to return. memory_store_id: Filter sessions whose resources contain a memory_store with this memory store ID. order: Sort direction for results, ordered by created_at. Defaults to desc (newest first). page: Opaque pagination cursor from a previous response. statuses: Filter by session status. Repeat the parameter to match any of multiple statuses. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get_api_list( "/v1/sessions?beta=true", page=SyncBidirectionalPageCursor[BetaManagedAgentsSession], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "agent_id": agent_id, "agent_version": agent_version, "created_at_gt": created_at_gt, "created_at_gte": created_at_gte, "created_at_lt": created_at_lt, "created_at_lte": created_at_lte, "deployment_id": deployment_id, "include_archived": include_archived, "limit": limit, "memory_store_id": memory_store_id, "order": order, "page": page, "statuses": statuses, }, session_list_params.SessionListParams, ), ), model=BetaManagedAgentsSession, ) def delete( self, session_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsDeletedSession: """ Delete Session Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._delete( path_template("/v1/sessions/{session_id}?beta=true", session_id=session_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsDeletedSession, ) def archive( self, session_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsSession: """ Archive Session Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._post( path_template("/v1/sessions/{session_id}/archive?beta=true", session_id=session_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsSession, ) class AsyncSessions(AsyncAPIResource): @cached_property def events(self) -> AsyncEvents: return AsyncEvents(self._client) @cached_property def resources(self) -> AsyncResources: return AsyncResources(self._client) @cached_property def threads(self) -> AsyncThreads: return AsyncThreads(self._client) @cached_property def with_raw_response(self) -> AsyncSessionsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncSessionsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncSessionsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncSessionsWithStreamingResponse(self) async def create( self, *, agent: session_create_params.Agent, environment_id: str, initial_events: Iterable[session_create_params.InitialEvent] | Omit = omit, metadata: Dict[str, str] | Omit = omit, resources: Iterable[session_create_params.Resource] | Omit = omit, title: Optional[str] | Omit = omit, vault_ids: SequenceNotStr[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsSession: """Create Session Args: agent: Agent identifier. Accepts the `agent` ID string, which pins the latest version for the session, or an `agent` object with both id and version specified. environment_id: ID of the `environment` defining the container configuration for this session. initial_events: Initial events to send to the `session` at creation, processed in order. Supports `user.message` and `user.define_outcome` events. Maximum 50 events. metadata: Arbitrary key-value metadata attached to the session. Maximum 16 pairs, keys up to 64 chars, values up to 512 chars. resources: Resources (e.g. repositories, files) to mount into the session's container. title: Human-readable session title. vault_ids: Vault IDs for stored credentials the agent can use during the session. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._post( "/v1/sessions?beta=true", body=await async_maybe_transform( { "agent": agent, "environment_id": environment_id, "initial_events": initial_events, "metadata": metadata, "resources": resources, "title": title, "vault_ids": vault_ids, }, session_create_params.SessionCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsSession, ) async def retrieve( self, session_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsSession: """ Get Session Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._get( path_template("/v1/sessions/{session_id}?beta=true", session_id=session_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsSession, ) async def update( self, session_id: str, *, agent: BetaManagedAgentsSessionAgentUpdateParam | Omit = omit, metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, title: Optional[str] | Omit = omit, vault_ids: SequenceNotStr[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsSession: """Update Session Args: agent: Mid-session agent configuration update. Only `tools` and `mcp_servers` are updatable. Full replacement: the provided array becomes the new value. To preserve existing entries, GET the session, modify the array, and POST it back. metadata: Metadata patch. Set a key to a string to upsert it, or to null to delete it. Omit the field to preserve. title: Human-readable session title. vault_ids: Vault IDs (`vlt_*`) to attach to the session. Not yet supported; requests setting this field are rejected. Reserved for future use. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._post( path_template("/v1/sessions/{session_id}?beta=true", session_id=session_id), body=await async_maybe_transform( { "agent": agent, "metadata": metadata, "title": title, "vault_ids": vault_ids, }, session_update_params.SessionUpdateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsSession, ) def list( self, *, agent_id: str | Omit = omit, agent_version: int | Omit = omit, created_at_gt: Union[str, datetime] | Omit = omit, created_at_gte: Union[str, datetime] | Omit = omit, created_at_lt: Union[str, datetime] | Omit = omit, created_at_lte: Union[str, datetime] | Omit = omit, deployment_id: str | Omit = omit, include_archived: bool | Omit = omit, limit: int | Omit = omit, memory_store_id: str | Omit = omit, order: Literal["asc", "desc"] | Omit = omit, page: str | Omit = omit, statuses: List[Literal["rescheduling", "running", "idle", "terminated"]] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[BetaManagedAgentsSession, AsyncBidirectionalPageCursor[BetaManagedAgentsSession]]: """ List Sessions Args: agent_id: Filter sessions created with this agent ID. agent_version: Filter by agent version. Only applies when agent_id is also set. created_at_gt: Return sessions created after this time (exclusive). created_at_gte: Return sessions created at or after this time (inclusive). created_at_lt: Return sessions created before this time (exclusive). created_at_lte: Return sessions created at or before this time (inclusive). deployment_id: Filter sessions created by this deployment ID. include_archived: When true, includes archived sessions. Default: false (exclude archived). limit: Maximum number of results to return. memory_store_id: Filter sessions whose resources contain a memory_store with this memory store ID. order: Sort direction for results, ordered by created_at. Defaults to desc (newest first). page: Opaque pagination cursor from a previous response. statuses: Filter by session status. Repeat the parameter to match any of multiple statuses. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get_api_list( "/v1/sessions?beta=true", page=AsyncBidirectionalPageCursor[BetaManagedAgentsSession], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "agent_id": agent_id, "agent_version": agent_version, "created_at_gt": created_at_gt, "created_at_gte": created_at_gte, "created_at_lt": created_at_lt, "created_at_lte": created_at_lte, "deployment_id": deployment_id, "include_archived": include_archived, "limit": limit, "memory_store_id": memory_store_id, "order": order, "page": page, "statuses": statuses, }, session_list_params.SessionListParams, ), ), model=BetaManagedAgentsSession, ) async def delete( self, session_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsDeletedSession: """ Delete Session Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._delete( path_template("/v1/sessions/{session_id}?beta=true", session_id=session_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsDeletedSession, ) async def archive( self, session_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsSession: """ Archive Session Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._post( path_template("/v1/sessions/{session_id}/archive?beta=true", session_id=session_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsSession, ) class SessionsWithRawResponse: def __init__(self, sessions: Sessions) -> None: self._sessions = sessions self.create = _legacy_response.to_raw_response_wrapper( sessions.create, ) self.retrieve = _legacy_response.to_raw_response_wrapper( sessions.retrieve, ) self.update = _legacy_response.to_raw_response_wrapper( sessions.update, ) self.list = _legacy_response.to_raw_response_wrapper( sessions.list, ) self.delete = _legacy_response.to_raw_response_wrapper( sessions.delete, ) self.archive = _legacy_response.to_raw_response_wrapper( sessions.archive, ) @cached_property def events(self) -> EventsWithRawResponse: return EventsWithRawResponse(self._sessions.events) @cached_property def resources(self) -> ResourcesWithRawResponse: return ResourcesWithRawResponse(self._sessions.resources) @cached_property def threads(self) -> ThreadsWithRawResponse: return ThreadsWithRawResponse(self._sessions.threads) class AsyncSessionsWithRawResponse: def __init__(self, sessions: AsyncSessions) -> None: self._sessions = sessions self.create = _legacy_response.async_to_raw_response_wrapper( sessions.create, ) self.retrieve = _legacy_response.async_to_raw_response_wrapper( sessions.retrieve, ) self.update = _legacy_response.async_to_raw_response_wrapper( sessions.update, ) self.list = _legacy_response.async_to_raw_response_wrapper( sessions.list, ) self.delete = _legacy_response.async_to_raw_response_wrapper( sessions.delete, ) self.archive = _legacy_response.async_to_raw_response_wrapper( sessions.archive, ) @cached_property def events(self) -> AsyncEventsWithRawResponse: return AsyncEventsWithRawResponse(self._sessions.events) @cached_property def resources(self) -> AsyncResourcesWithRawResponse: return AsyncResourcesWithRawResponse(self._sessions.resources) @cached_property def threads(self) -> AsyncThreadsWithRawResponse: return AsyncThreadsWithRawResponse(self._sessions.threads) class SessionsWithStreamingResponse: def __init__(self, sessions: Sessions) -> None: self._sessions = sessions self.create = to_streamed_response_wrapper( sessions.create, ) self.retrieve = to_streamed_response_wrapper( sessions.retrieve, ) self.update = to_streamed_response_wrapper( sessions.update, ) self.list = to_streamed_response_wrapper( sessions.list, ) self.delete = to_streamed_response_wrapper( sessions.delete, ) self.archive = to_streamed_response_wrapper( sessions.archive, ) @cached_property def events(self) -> EventsWithStreamingResponse: return EventsWithStreamingResponse(self._sessions.events) @cached_property def resources(self) -> ResourcesWithStreamingResponse: return ResourcesWithStreamingResponse(self._sessions.resources) @cached_property def threads(self) -> ThreadsWithStreamingResponse: return ThreadsWithStreamingResponse(self._sessions.threads) class AsyncSessionsWithStreamingResponse: def __init__(self, sessions: AsyncSessions) -> None: self._sessions = sessions self.create = async_to_streamed_response_wrapper( sessions.create, ) self.retrieve = async_to_streamed_response_wrapper( sessions.retrieve, ) self.update = async_to_streamed_response_wrapper( sessions.update, ) self.list = async_to_streamed_response_wrapper( sessions.list, ) self.delete = async_to_streamed_response_wrapper( sessions.delete, ) self.archive = async_to_streamed_response_wrapper( sessions.archive, ) @cached_property def events(self) -> AsyncEventsWithStreamingResponse: return AsyncEventsWithStreamingResponse(self._sessions.events) @cached_property def resources(self) -> AsyncResourcesWithStreamingResponse: return AsyncResourcesWithStreamingResponse(self._sessions.resources) @cached_property def threads(self) -> AsyncThreadsWithStreamingResponse: return AsyncThreadsWithStreamingResponse(self._sessions.threads) anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/sessions/threads/000077500000000000000000000000001523216435200267475ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/sessions/threads/__init__.py000066400000000000000000000014671523216435200310700ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .events import ( Events, AsyncEvents, EventsWithRawResponse, AsyncEventsWithRawResponse, EventsWithStreamingResponse, AsyncEventsWithStreamingResponse, ) from .threads import ( Threads, AsyncThreads, ThreadsWithRawResponse, AsyncThreadsWithRawResponse, ThreadsWithStreamingResponse, AsyncThreadsWithStreamingResponse, ) __all__ = [ "Events", "AsyncEvents", "EventsWithRawResponse", "AsyncEventsWithRawResponse", "EventsWithStreamingResponse", "AsyncEventsWithStreamingResponse", "Threads", "AsyncThreads", "ThreadsWithRawResponse", "AsyncThreadsWithRawResponse", "ThreadsWithStreamingResponse", "AsyncThreadsWithStreamingResponse", ] anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/sessions/threads/events.py000066400000000000000000000412161523216435200306310ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Any, List, cast from itertools import chain import httpx from ..... import _legacy_response from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ....._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform from ....._compat import cached_property from ....._resource import SyncAPIResource, AsyncAPIResource from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ....._streaming import Stream, AsyncStream from .....pagination import SyncPageCursor, AsyncPageCursor from ....._base_client import AsyncPaginator, make_request_options from .....types.anthropic_beta_param import AnthropicBetaParam from .....types.beta.sessions.threads import event_list_params, event_stream_params from .....types.beta.beta_managed_agents_delta_type import BetaManagedAgentsDeltaType from .....types.beta.sessions.beta_managed_agents_session_event import BetaManagedAgentsSessionEvent from .....types.beta.sessions.beta_managed_agents_stream_session_thread_events import ( BetaManagedAgentsStreamSessionThreadEvents, ) __all__ = ["Events", "AsyncEvents"] class Events(SyncAPIResource): @cached_property def with_raw_response(self) -> EventsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return EventsWithRawResponse(self) @cached_property def with_streaming_response(self) -> EventsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return EventsWithStreamingResponse(self) def list( self, thread_id: str, *, session_id: str, limit: int | Omit = omit, page: str | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncPageCursor[BetaManagedAgentsSessionEvent]: """ List Session Thread Events Args: limit: Query parameter for limit page: Query parameter for page betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") if not thread_id: raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get_api_list( path_template( "/v1/sessions/{session_id}/threads/{thread_id}/events?beta=true", session_id=session_id, thread_id=thread_id, ), page=SyncPageCursor[BetaManagedAgentsSessionEvent], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "limit": limit, "page": page, }, event_list_params.EventListParams, ), ), model=cast( Any, BetaManagedAgentsSessionEvent ), # Union types cannot be passed in as arguments in the type system ) def stream( self, thread_id: str, *, session_id: str, event_deltas: List[BetaManagedAgentsDeltaType] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Stream[BetaManagedAgentsStreamSessionThreadEvents]: """ Stream Session Thread Events Args: event_deltas: When set, this connection also receives streaming deltas (`event_start`, `event_delta`) while an event is being produced, before the event itself arrives. Deltas are best-effort; when the final event is produced it carries the complete content. A model request that ends early (an error or interrupt) produces no final event — its terminal `span.model_request_end` closes the preview. Accepts one or more event types to preview and may be repeated: `agent.message` streams `content_delta` fragments; `agent.thinking` is start-only — a signal that the agent has begun extended thinking, concluded by the `agent.thinking` event itself. Only previews of the requested event types are sent. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") if not thread_id: raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get( path_template( "/v1/sessions/{session_id}/threads/{thread_id}/stream?beta=true", session_id=session_id, thread_id=thread_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform({"event_deltas": event_deltas}, event_stream_params.EventStreamParams), ), cast_to=cast( Any, BetaManagedAgentsStreamSessionThreadEvents ), # Union types cannot be passed in as arguments in the type system stream=True, stream_cls=Stream[BetaManagedAgentsStreamSessionThreadEvents], ) class AsyncEvents(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncEventsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncEventsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncEventsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncEventsWithStreamingResponse(self) def list( self, thread_id: str, *, session_id: str, limit: int | Omit = omit, page: str | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[BetaManagedAgentsSessionEvent, AsyncPageCursor[BetaManagedAgentsSessionEvent]]: """ List Session Thread Events Args: limit: Query parameter for limit page: Query parameter for page betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") if not thread_id: raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get_api_list( path_template( "/v1/sessions/{session_id}/threads/{thread_id}/events?beta=true", session_id=session_id, thread_id=thread_id, ), page=AsyncPageCursor[BetaManagedAgentsSessionEvent], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "limit": limit, "page": page, }, event_list_params.EventListParams, ), ), model=cast( Any, BetaManagedAgentsSessionEvent ), # Union types cannot be passed in as arguments in the type system ) async def stream( self, thread_id: str, *, session_id: str, event_deltas: List[BetaManagedAgentsDeltaType] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncStream[BetaManagedAgentsStreamSessionThreadEvents]: """ Stream Session Thread Events Args: event_deltas: When set, this connection also receives streaming deltas (`event_start`, `event_delta`) while an event is being produced, before the event itself arrives. Deltas are best-effort; when the final event is produced it carries the complete content. A model request that ends early (an error or interrupt) produces no final event — its terminal `span.model_request_end` closes the preview. Accepts one or more event types to preview and may be repeated: `agent.message` streams `content_delta` fragments; `agent.thinking` is start-only — a signal that the agent has begun extended thinking, concluded by the `agent.thinking` event itself. Only previews of the requested event types are sent. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") if not thread_id: raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._get( path_template( "/v1/sessions/{session_id}/threads/{thread_id}/stream?beta=true", session_id=session_id, thread_id=thread_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=await async_maybe_transform( {"event_deltas": event_deltas}, event_stream_params.EventStreamParams ), ), cast_to=cast( Any, BetaManagedAgentsStreamSessionThreadEvents ), # Union types cannot be passed in as arguments in the type system stream=True, stream_cls=AsyncStream[BetaManagedAgentsStreamSessionThreadEvents], ) class EventsWithRawResponse: def __init__(self, events: Events) -> None: self._events = events self.list = _legacy_response.to_raw_response_wrapper( events.list, ) self.stream = _legacy_response.to_raw_response_wrapper( events.stream, ) class AsyncEventsWithRawResponse: def __init__(self, events: AsyncEvents) -> None: self._events = events self.list = _legacy_response.async_to_raw_response_wrapper( events.list, ) self.stream = _legacy_response.async_to_raw_response_wrapper( events.stream, ) class EventsWithStreamingResponse: def __init__(self, events: Events) -> None: self._events = events self.list = to_streamed_response_wrapper( events.list, ) self.stream = to_streamed_response_wrapper( events.stream, ) class AsyncEventsWithStreamingResponse: def __init__(self, events: AsyncEvents) -> None: self._events = events self.list = async_to_streamed_response_wrapper( events.list, ) self.stream = async_to_streamed_response_wrapper( events.stream, ) anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/sessions/threads/threads.py000066400000000000000000000453161523216435200307640ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List from itertools import chain import httpx from ..... import _legacy_response from .events import ( Events, AsyncEvents, EventsWithRawResponse, AsyncEventsWithRawResponse, EventsWithStreamingResponse, AsyncEventsWithStreamingResponse, ) from ....._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ....._utils import is_given, path_template, maybe_transform, strip_not_given from ....._compat import cached_property from ....._resource import SyncAPIResource, AsyncAPIResource from ....._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from .....pagination import SyncPageCursor, AsyncPageCursor from ....._base_client import AsyncPaginator, make_request_options from .....types.beta.sessions import thread_list_params from .....types.anthropic_beta_param import AnthropicBetaParam from .....types.beta.sessions.beta_managed_agents_session_thread import BetaManagedAgentsSessionThread __all__ = ["Threads", "AsyncThreads"] class Threads(SyncAPIResource): @cached_property def events(self) -> Events: return Events(self._client) @cached_property def with_raw_response(self) -> ThreadsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return ThreadsWithRawResponse(self) @cached_property def with_streaming_response(self) -> ThreadsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return ThreadsWithStreamingResponse(self) def retrieve( self, thread_id: str, *, session_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsSessionThread: """ Get Session Thread Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") if not thread_id: raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get( path_template( "/v1/sessions/{session_id}/threads/{thread_id}?beta=true", session_id=session_id, thread_id=thread_id ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsSessionThread, ) def list( self, session_id: str, *, limit: int | Omit = omit, page: str | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncPageCursor[BetaManagedAgentsSessionThread]: """List Session Threads Args: limit: Maximum results per page. Defaults to 1000. page: Opaque pagination cursor from a previous response's next_page. Forward-only. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get_api_list( path_template("/v1/sessions/{session_id}/threads?beta=true", session_id=session_id), page=SyncPageCursor[BetaManagedAgentsSessionThread], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "limit": limit, "page": page, }, thread_list_params.ThreadListParams, ), ), model=BetaManagedAgentsSessionThread, ) def archive( self, thread_id: str, *, session_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsSessionThread: """ Archive Session Thread Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") if not thread_id: raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._post( path_template( "/v1/sessions/{session_id}/threads/{thread_id}/archive?beta=true", session_id=session_id, thread_id=thread_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsSessionThread, ) class AsyncThreads(AsyncAPIResource): @cached_property def events(self) -> AsyncEvents: return AsyncEvents(self._client) @cached_property def with_raw_response(self) -> AsyncThreadsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncThreadsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncThreadsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncThreadsWithStreamingResponse(self) async def retrieve( self, thread_id: str, *, session_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsSessionThread: """ Get Session Thread Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") if not thread_id: raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._get( path_template( "/v1/sessions/{session_id}/threads/{thread_id}?beta=true", session_id=session_id, thread_id=thread_id ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsSessionThread, ) def list( self, session_id: str, *, limit: int | Omit = omit, page: str | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[BetaManagedAgentsSessionThread, AsyncPageCursor[BetaManagedAgentsSessionThread]]: """List Session Threads Args: limit: Maximum results per page. Defaults to 1000. page: Opaque pagination cursor from a previous response's next_page. Forward-only. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get_api_list( path_template("/v1/sessions/{session_id}/threads?beta=true", session_id=session_id), page=AsyncPageCursor[BetaManagedAgentsSessionThread], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "limit": limit, "page": page, }, thread_list_params.ThreadListParams, ), ), model=BetaManagedAgentsSessionThread, ) async def archive( self, thread_id: str, *, session_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsSessionThread: """ Archive Session Thread Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not session_id: raise ValueError(f"Expected a non-empty value for `session_id` but received {session_id!r}") if not thread_id: raise ValueError(f"Expected a non-empty value for `thread_id` but received {thread_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._post( path_template( "/v1/sessions/{session_id}/threads/{thread_id}/archive?beta=true", session_id=session_id, thread_id=thread_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsSessionThread, ) class ThreadsWithRawResponse: def __init__(self, threads: Threads) -> None: self._threads = threads self.retrieve = _legacy_response.to_raw_response_wrapper( threads.retrieve, ) self.list = _legacy_response.to_raw_response_wrapper( threads.list, ) self.archive = _legacy_response.to_raw_response_wrapper( threads.archive, ) @cached_property def events(self) -> EventsWithRawResponse: return EventsWithRawResponse(self._threads.events) class AsyncThreadsWithRawResponse: def __init__(self, threads: AsyncThreads) -> None: self._threads = threads self.retrieve = _legacy_response.async_to_raw_response_wrapper( threads.retrieve, ) self.list = _legacy_response.async_to_raw_response_wrapper( threads.list, ) self.archive = _legacy_response.async_to_raw_response_wrapper( threads.archive, ) @cached_property def events(self) -> AsyncEventsWithRawResponse: return AsyncEventsWithRawResponse(self._threads.events) class ThreadsWithStreamingResponse: def __init__(self, threads: Threads) -> None: self._threads = threads self.retrieve = to_streamed_response_wrapper( threads.retrieve, ) self.list = to_streamed_response_wrapper( threads.list, ) self.archive = to_streamed_response_wrapper( threads.archive, ) @cached_property def events(self) -> EventsWithStreamingResponse: return EventsWithStreamingResponse(self._threads.events) class AsyncThreadsWithStreamingResponse: def __init__(self, threads: AsyncThreads) -> None: self._threads = threads self.retrieve = async_to_streamed_response_wrapper( threads.retrieve, ) self.list = async_to_streamed_response_wrapper( threads.list, ) self.archive = async_to_streamed_response_wrapper( threads.archive, ) @cached_property def events(self) -> AsyncEventsWithStreamingResponse: return AsyncEventsWithStreamingResponse(self._threads.events) anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/skills/000077500000000000000000000000001523216435200247505ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/skills/__init__.py000066400000000000000000000015041523216435200270610ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .skills import ( Skills, AsyncSkills, SkillsWithRawResponse, AsyncSkillsWithRawResponse, SkillsWithStreamingResponse, AsyncSkillsWithStreamingResponse, ) from .versions import ( Versions, AsyncVersions, VersionsWithRawResponse, AsyncVersionsWithRawResponse, VersionsWithStreamingResponse, AsyncVersionsWithStreamingResponse, ) __all__ = [ "Versions", "AsyncVersions", "VersionsWithRawResponse", "AsyncVersionsWithRawResponse", "VersionsWithStreamingResponse", "AsyncVersionsWithStreamingResponse", "Skills", "AsyncSkills", "SkillsWithRawResponse", "AsyncSkillsWithRawResponse", "SkillsWithStreamingResponse", "AsyncSkillsWithStreamingResponse", ] anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/skills/skills.py000066400000000000000000000611731523216435200266330ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Mapping, Optional, cast from itertools import chain import httpx from .... import _legacy_response from .versions import ( Versions, AsyncVersions, VersionsWithRawResponse, AsyncVersionsWithRawResponse, VersionsWithStreamingResponse, AsyncVersionsWithStreamingResponse, ) from ...._files import deepcopy_with_paths from ...._types import ( Body, Omit, Query, Headers, NotGiven, FileTypes, SequenceNotStr, omit, not_given, ) from ...._utils import is_given, extract_files, path_template, maybe_transform, strip_not_given, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ....pagination import SyncPageCursor, AsyncPageCursor from ....types.beta import skill_list_params, skill_create_params from ...._base_client import AsyncPaginator, make_request_options from ....types.anthropic_beta_param import AnthropicBetaParam from ....types.beta.skill_list_response import SkillListResponse from ....types.beta.skill_create_response import SkillCreateResponse from ....types.beta.skill_delete_response import SkillDeleteResponse from ....types.beta.skill_retrieve_response import SkillRetrieveResponse __all__ = ["Skills", "AsyncSkills"] class Skills(SyncAPIResource): @cached_property def versions(self) -> Versions: return Versions(self._client) @cached_property def with_raw_response(self) -> SkillsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return SkillsWithRawResponse(self) @cached_property def with_streaming_response(self) -> SkillsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return SkillsWithStreamingResponse(self) def create( self, *, files: SequenceNotStr[FileTypes], display_title: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SkillCreateResponse: """ Create Skill Args: files: Files to upload for the skill. All files must be in the same top-level directory and must include a SKILL.md file at the root of that directory. display_title: Display title for the skill. This is a human-readable label that is not included in the prompt sent to the model. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} body = deepcopy_with_paths( { "files": files, "display_title": display_title, }, [["files", ""]], ) extracted_files = extract_files(cast(Mapping[str, object], body), paths=[["files", ""]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers["Content-Type"] = "multipart/form-data" return self._post( "/v1/skills?beta=true", body=maybe_transform(body, skill_create_params.SkillCreateParams), files=extracted_files, options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=SkillCreateResponse, ) def retrieve( self, skill_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SkillRetrieveResponse: """ Get Skill Args: skill_id: Unique identifier for the skill. The format and length of IDs may change over time. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not skill_id: raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} return self._get( path_template("/v1/skills/{skill_id}?beta=true", skill_id=skill_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=SkillRetrieveResponse, ) def list( self, *, limit: int | Omit = omit, page: Optional[str] | Omit = omit, source: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncPageCursor[SkillListResponse]: """ List Skills Args: limit: Number of results to return per page. Maximum value is 100. Defaults to 20. page: Pagination token for fetching a specific page of results. Pass the value from a previous response's `next_page` field to get the next page of results. source: Filter skills by source. If provided, only skills from the specified source will be returned: - `"custom"`: only return user-created skills - `"anthropic"`: only return Anthropic-created skills betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} return self._get_api_list( "/v1/skills?beta=true", page=SyncPageCursor[SkillListResponse], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "limit": limit, "page": page, "source": source, }, skill_list_params.SkillListParams, ), ), model=SkillListResponse, ) def delete( self, skill_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SkillDeleteResponse: """ Delete Skill Args: skill_id: Unique identifier for the skill. The format and length of IDs may change over time. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not skill_id: raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} return self._delete( path_template("/v1/skills/{skill_id}?beta=true", skill_id=skill_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=SkillDeleteResponse, ) class AsyncSkills(AsyncAPIResource): @cached_property def versions(self) -> AsyncVersions: return AsyncVersions(self._client) @cached_property def with_raw_response(self) -> AsyncSkillsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncSkillsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncSkillsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncSkillsWithStreamingResponse(self) async def create( self, *, files: SequenceNotStr[FileTypes], display_title: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SkillCreateResponse: """ Create Skill Args: files: Files to upload for the skill. All files must be in the same top-level directory and must include a SKILL.md file at the root of that directory. display_title: Display title for the skill. This is a human-readable label that is not included in the prompt sent to the model. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} body = deepcopy_with_paths( { "files": files, "display_title": display_title, }, [["files", ""]], ) extracted_files = extract_files(cast(Mapping[str, object], body), paths=[["files", ""]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers["Content-Type"] = "multipart/form-data" return await self._post( "/v1/skills?beta=true", body=await async_maybe_transform(body, skill_create_params.SkillCreateParams), files=extracted_files, options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=SkillCreateResponse, ) async def retrieve( self, skill_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SkillRetrieveResponse: """ Get Skill Args: skill_id: Unique identifier for the skill. The format and length of IDs may change over time. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not skill_id: raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} return await self._get( path_template("/v1/skills/{skill_id}?beta=true", skill_id=skill_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=SkillRetrieveResponse, ) def list( self, *, limit: int | Omit = omit, page: Optional[str] | Omit = omit, source: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[SkillListResponse, AsyncPageCursor[SkillListResponse]]: """ List Skills Args: limit: Number of results to return per page. Maximum value is 100. Defaults to 20. page: Pagination token for fetching a specific page of results. Pass the value from a previous response's `next_page` field to get the next page of results. source: Filter skills by source. If provided, only skills from the specified source will be returned: - `"custom"`: only return user-created skills - `"anthropic"`: only return Anthropic-created skills betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} return self._get_api_list( "/v1/skills?beta=true", page=AsyncPageCursor[SkillListResponse], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "limit": limit, "page": page, "source": source, }, skill_list_params.SkillListParams, ), ), model=SkillListResponse, ) async def delete( self, skill_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SkillDeleteResponse: """ Delete Skill Args: skill_id: Unique identifier for the skill. The format and length of IDs may change over time. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not skill_id: raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} return await self._delete( path_template("/v1/skills/{skill_id}?beta=true", skill_id=skill_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=SkillDeleteResponse, ) class SkillsWithRawResponse: def __init__(self, skills: Skills) -> None: self._skills = skills self.create = _legacy_response.to_raw_response_wrapper( skills.create, ) self.retrieve = _legacy_response.to_raw_response_wrapper( skills.retrieve, ) self.list = _legacy_response.to_raw_response_wrapper( skills.list, ) self.delete = _legacy_response.to_raw_response_wrapper( skills.delete, ) @cached_property def versions(self) -> VersionsWithRawResponse: return VersionsWithRawResponse(self._skills.versions) class AsyncSkillsWithRawResponse: def __init__(self, skills: AsyncSkills) -> None: self._skills = skills self.create = _legacy_response.async_to_raw_response_wrapper( skills.create, ) self.retrieve = _legacy_response.async_to_raw_response_wrapper( skills.retrieve, ) self.list = _legacy_response.async_to_raw_response_wrapper( skills.list, ) self.delete = _legacy_response.async_to_raw_response_wrapper( skills.delete, ) @cached_property def versions(self) -> AsyncVersionsWithRawResponse: return AsyncVersionsWithRawResponse(self._skills.versions) class SkillsWithStreamingResponse: def __init__(self, skills: Skills) -> None: self._skills = skills self.create = to_streamed_response_wrapper( skills.create, ) self.retrieve = to_streamed_response_wrapper( skills.retrieve, ) self.list = to_streamed_response_wrapper( skills.list, ) self.delete = to_streamed_response_wrapper( skills.delete, ) @cached_property def versions(self) -> VersionsWithStreamingResponse: return VersionsWithStreamingResponse(self._skills.versions) class AsyncSkillsWithStreamingResponse: def __init__(self, skills: AsyncSkills) -> None: self._skills = skills self.create = async_to_streamed_response_wrapper( skills.create, ) self.retrieve = async_to_streamed_response_wrapper( skills.retrieve, ) self.list = async_to_streamed_response_wrapper( skills.list, ) self.delete = async_to_streamed_response_wrapper( skills.delete, ) @cached_property def versions(self) -> AsyncVersionsWithStreamingResponse: return AsyncVersionsWithStreamingResponse(self._skills.versions) anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/skills/versions.py000066400000000000000000000752561523216435200272110ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Mapping, Optional, cast from itertools import chain import httpx from .... import _legacy_response from ...._files import deepcopy_with_paths from ...._types import ( Body, Omit, Query, Headers, NotGiven, FileTypes, SequenceNotStr, omit, not_given, ) from ...._utils import is_given, extract_files, path_template, maybe_transform, strip_not_given, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import ( BinaryAPIResponse, AsyncBinaryAPIResponse, StreamedBinaryAPIResponse, AsyncStreamedBinaryAPIResponse, to_streamed_response_wrapper, to_custom_raw_response_wrapper, async_to_streamed_response_wrapper, to_custom_streamed_response_wrapper, async_to_custom_raw_response_wrapper, async_to_custom_streamed_response_wrapper, ) from ....pagination import SyncPageCursor, AsyncPageCursor from ...._base_client import AsyncPaginator, make_request_options from ....types.beta.skills import version_list_params, version_create_params from ....types.anthropic_beta_param import AnthropicBetaParam from ....types.beta.skills.version_list_response import VersionListResponse from ....types.beta.skills.version_create_response import VersionCreateResponse from ....types.beta.skills.version_delete_response import VersionDeleteResponse from ....types.beta.skills.version_retrieve_response import VersionRetrieveResponse __all__ = ["Versions", "AsyncVersions"] class Versions(SyncAPIResource): @cached_property def with_raw_response(self) -> VersionsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return VersionsWithRawResponse(self) @cached_property def with_streaming_response(self) -> VersionsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return VersionsWithStreamingResponse(self) def create( self, skill_id: str, *, files: SequenceNotStr[FileTypes], betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VersionCreateResponse: """ Create Skill Version Args: skill_id: Unique identifier for the skill. The format and length of IDs may change over time. files: Files to upload for the skill. All files must be in the same top-level directory and must include a SKILL.md file at the root of that directory. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not skill_id: raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} body = deepcopy_with_paths({"files": files}, [["files", ""]]) extracted_files = extract_files(cast(Mapping[str, object], body), paths=[["files", ""]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers["Content-Type"] = "multipart/form-data" return self._post( path_template("/v1/skills/{skill_id}/versions?beta=true", skill_id=skill_id), body=maybe_transform(body, version_create_params.VersionCreateParams), files=extracted_files, options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=VersionCreateResponse, ) def retrieve( self, version: str, *, skill_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VersionRetrieveResponse: """ Get Skill Version Args: skill_id: Unique identifier for the skill. The format and length of IDs may change over time. version: Version identifier for the skill. Each version is identified by a Unix epoch timestamp (e.g., "1759178010641129"). betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not skill_id: raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") if not version: raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} return self._get( path_template("/v1/skills/{skill_id}/versions/{version}?beta=true", skill_id=skill_id, version=version), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=VersionRetrieveResponse, ) def list( self, skill_id: str, *, limit: Optional[int] | Omit = omit, page: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncPageCursor[VersionListResponse]: """ List Skill Versions Args: skill_id: Unique identifier for the skill. The format and length of IDs may change over time. limit: Number of items to return per page. Defaults to `20`. Ranges from `1` to `1000`. page: Optionally set to the `next_page` token from the previous response. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not skill_id: raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} return self._get_api_list( path_template("/v1/skills/{skill_id}/versions?beta=true", skill_id=skill_id), page=SyncPageCursor[VersionListResponse], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "limit": limit, "page": page, }, version_list_params.VersionListParams, ), ), model=VersionListResponse, ) def delete( self, version: str, *, skill_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VersionDeleteResponse: """ Delete Skill Version Args: skill_id: Unique identifier for the skill. The format and length of IDs may change over time. version: Version identifier for the skill. Each version is identified by a Unix epoch timestamp (e.g., "1759178010641129"). betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not skill_id: raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") if not version: raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} return self._delete( path_template("/v1/skills/{skill_id}/versions/{version}?beta=true", skill_id=skill_id, version=version), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=VersionDeleteResponse, ) def download( self, version: str, *, skill_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BinaryAPIResponse: """ Download a skill version's content as a zip archive. Args: skill_id: Unique identifier for the skill. The format and length of IDs may change over time. version: Version identifier for the skill. Each version is identified by a Unix epoch timestamp (e.g., "1759178010641129"). betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not skill_id: raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") if not version: raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") extra_headers = {"Accept": "application/binary", **(extra_headers or {})} extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} return self._get( path_template( "/v1/skills/{skill_id}/versions/{version}/content?beta=true", skill_id=skill_id, version=version ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BinaryAPIResponse, ) class AsyncVersions(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncVersionsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncVersionsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncVersionsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncVersionsWithStreamingResponse(self) async def create( self, skill_id: str, *, files: SequenceNotStr[FileTypes], betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VersionCreateResponse: """ Create Skill Version Args: skill_id: Unique identifier for the skill. The format and length of IDs may change over time. files: Files to upload for the skill. All files must be in the same top-level directory and must include a SKILL.md file at the root of that directory. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not skill_id: raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} body = deepcopy_with_paths({"files": files}, [["files", ""]]) extracted_files = extract_files(cast(Mapping[str, object], body), paths=[["files", ""]]) # It should be noted that the actual Content-Type header that will be # sent to the server will contain a `boundary` parameter, e.g. # multipart/form-data; boundary=---abc-- extra_headers["Content-Type"] = "multipart/form-data" return await self._post( path_template("/v1/skills/{skill_id}/versions?beta=true", skill_id=skill_id), body=await async_maybe_transform(body, version_create_params.VersionCreateParams), files=extracted_files, options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=VersionCreateResponse, ) async def retrieve( self, version: str, *, skill_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VersionRetrieveResponse: """ Get Skill Version Args: skill_id: Unique identifier for the skill. The format and length of IDs may change over time. version: Version identifier for the skill. Each version is identified by a Unix epoch timestamp (e.g., "1759178010641129"). betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not skill_id: raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") if not version: raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} return await self._get( path_template("/v1/skills/{skill_id}/versions/{version}?beta=true", skill_id=skill_id, version=version), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=VersionRetrieveResponse, ) def list( self, skill_id: str, *, limit: Optional[int] | Omit = omit, page: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[VersionListResponse, AsyncPageCursor[VersionListResponse]]: """ List Skill Versions Args: skill_id: Unique identifier for the skill. The format and length of IDs may change over time. limit: Number of items to return per page. Defaults to `20`. Ranges from `1` to `1000`. page: Optionally set to the `next_page` token from the previous response. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not skill_id: raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} return self._get_api_list( path_template("/v1/skills/{skill_id}/versions?beta=true", skill_id=skill_id), page=AsyncPageCursor[VersionListResponse], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "limit": limit, "page": page, }, version_list_params.VersionListParams, ), ), model=VersionListResponse, ) async def delete( self, version: str, *, skill_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> VersionDeleteResponse: """ Delete Skill Version Args: skill_id: Unique identifier for the skill. The format and length of IDs may change over time. version: Version identifier for the skill. Each version is identified by a Unix epoch timestamp (e.g., "1759178010641129"). betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not skill_id: raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") if not version: raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} return await self._delete( path_template("/v1/skills/{skill_id}/versions/{version}?beta=true", skill_id=skill_id, version=version), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=VersionDeleteResponse, ) async def download( self, version: str, *, skill_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncBinaryAPIResponse: """ Download a skill version's content as a zip archive. Args: skill_id: Unique identifier for the skill. The format and length of IDs may change over time. version: Version identifier for the skill. Each version is identified by a Unix epoch timestamp (e.g., "1759178010641129"). betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not skill_id: raise ValueError(f"Expected a non-empty value for `skill_id` but received {skill_id!r}") if not version: raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") extra_headers = {"Accept": "application/binary", **(extra_headers or {})} extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["skills-2025-10-02"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "skills-2025-10-02", **(extra_headers or {})} return await self._get( path_template( "/v1/skills/{skill_id}/versions/{version}/content?beta=true", skill_id=skill_id, version=version ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=AsyncBinaryAPIResponse, ) class VersionsWithRawResponse: def __init__(self, versions: Versions) -> None: self._versions = versions self.create = _legacy_response.to_raw_response_wrapper( versions.create, ) self.retrieve = _legacy_response.to_raw_response_wrapper( versions.retrieve, ) self.list = _legacy_response.to_raw_response_wrapper( versions.list, ) self.delete = _legacy_response.to_raw_response_wrapper( versions.delete, ) self.download = to_custom_raw_response_wrapper( versions.download, BinaryAPIResponse, ) class AsyncVersionsWithRawResponse: def __init__(self, versions: AsyncVersions) -> None: self._versions = versions self.create = _legacy_response.async_to_raw_response_wrapper( versions.create, ) self.retrieve = _legacy_response.async_to_raw_response_wrapper( versions.retrieve, ) self.list = _legacy_response.async_to_raw_response_wrapper( versions.list, ) self.delete = _legacy_response.async_to_raw_response_wrapper( versions.delete, ) self.download = async_to_custom_raw_response_wrapper( versions.download, AsyncBinaryAPIResponse, ) class VersionsWithStreamingResponse: def __init__(self, versions: Versions) -> None: self._versions = versions self.create = to_streamed_response_wrapper( versions.create, ) self.retrieve = to_streamed_response_wrapper( versions.retrieve, ) self.list = to_streamed_response_wrapper( versions.list, ) self.delete = to_streamed_response_wrapper( versions.delete, ) self.download = to_custom_streamed_response_wrapper( versions.download, StreamedBinaryAPIResponse, ) class AsyncVersionsWithStreamingResponse: def __init__(self, versions: AsyncVersions) -> None: self._versions = versions self.create = async_to_streamed_response_wrapper( versions.create, ) self.retrieve = async_to_streamed_response_wrapper( versions.retrieve, ) self.list = async_to_streamed_response_wrapper( versions.list, ) self.delete = async_to_streamed_response_wrapper( versions.delete, ) self.download = async_to_custom_streamed_response_wrapper( versions.download, AsyncStreamedBinaryAPIResponse, ) anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/tunnels/000077500000000000000000000000001523216435200251375ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/tunnels/__init__.py000066400000000000000000000016051523216435200272520ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .tunnels import ( Tunnels, AsyncTunnels, TunnelsWithRawResponse, AsyncTunnelsWithRawResponse, TunnelsWithStreamingResponse, AsyncTunnelsWithStreamingResponse, ) from .certificates import ( Certificates, AsyncCertificates, CertificatesWithRawResponse, AsyncCertificatesWithRawResponse, CertificatesWithStreamingResponse, AsyncCertificatesWithStreamingResponse, ) __all__ = [ "Certificates", "AsyncCertificates", "CertificatesWithRawResponse", "AsyncCertificatesWithRawResponse", "CertificatesWithStreamingResponse", "AsyncCertificatesWithStreamingResponse", "Tunnels", "AsyncTunnels", "TunnelsWithRawResponse", "AsyncTunnelsWithRawResponse", "TunnelsWithStreamingResponse", "AsyncTunnelsWithStreamingResponse", ] anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/tunnels/certificates.py000066400000000000000000000656241523216435200301730ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List from itertools import chain import httpx from .... import _legacy_response from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ...._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ....pagination import SyncPageCursor, AsyncPageCursor from ...._base_client import AsyncPaginator, make_request_options from ....types.beta.tunnels import certificate_list_params, certificate_create_params from ....types.anthropic_beta_param import AnthropicBetaParam from ....types.beta.tunnels.beta_tunnel_certificate import BetaTunnelCertificate __all__ = ["Certificates", "AsyncCertificates"] class Certificates(SyncAPIResource): @cached_property def with_raw_response(self) -> CertificatesWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return CertificatesWithRawResponse(self) @cached_property def with_streaming_response(self) -> CertificatesWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return CertificatesWithStreamingResponse(self) def create( self, tunnel_id: str, *, ca_certificate_pem: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaTunnelCertificate: """The Tunnels API is in research preview. It requires the `anthropic-beta: mcp-tunnels-2026-06-22` header and may change without a deprecation period. It supersedes the Admin API endpoints at `/v1/organizations/tunnels`, which remain available during a migration window. Registers a public CA certificate on a tunnel. Anthropic verifies the gateway's server certificate against this CA when it terminates the inner TLS session. A tunnel holds at most two non-archived certificates. Args: ca_certificate_pem: PEM-encoded X.509 CA certificate. Must contain exactly one certificate and no private-key material. Maximum 8KB. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not tunnel_id: raise ValueError(f"Expected a non-empty value for `tunnel_id` but received {tunnel_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["mcp-tunnels-2026-06-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "mcp-tunnels-2026-06-22", **(extra_headers or {})} return self._post( path_template("/v1/tunnels/{tunnel_id}/certificates?beta=true", tunnel_id=tunnel_id), body=maybe_transform( {"ca_certificate_pem": ca_certificate_pem}, certificate_create_params.CertificateCreateParams ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaTunnelCertificate, ) def retrieve( self, certificate_id: str, *, tunnel_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaTunnelCertificate: """The Tunnels API is in research preview. It requires the `anthropic-beta: mcp-tunnels-2026-06-22` header and may change without a deprecation period. It supersedes the Admin API endpoints at `/v1/organizations/tunnels`, which remain available during a migration window. Fetches a tunnel certificate by ID. Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not tunnel_id: raise ValueError(f"Expected a non-empty value for `tunnel_id` but received {tunnel_id!r}") if not certificate_id: raise ValueError(f"Expected a non-empty value for `certificate_id` but received {certificate_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["mcp-tunnels-2026-06-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "mcp-tunnels-2026-06-22", **(extra_headers or {})} return self._get( path_template( "/v1/tunnels/{tunnel_id}/certificates/{certificate_id}?beta=true", tunnel_id=tunnel_id, certificate_id=certificate_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaTunnelCertificate, ) def list( self, tunnel_id: str, *, include_archived: bool | Omit = omit, limit: int | Omit = omit, page: str | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncPageCursor[BetaTunnelCertificate]: """The Tunnels API is in research preview. It requires the `anthropic-beta: mcp-tunnels-2026-06-22` header and may change without a deprecation period. It supersedes the Admin API endpoints at `/v1/organizations/tunnels`, which remain available during a migration window. Lists the certificates registered on a tunnel. Archived certificates are excluded unless include_archived is set. Args: include_archived: Whether to include archived certificates in the results. Defaults to false. limit: Maximum number of certificates to return per page. Defaults to 20, maximum 1000. page: Opaque pagination cursor from a previous `list_tunnel_certificates` response. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not tunnel_id: raise ValueError(f"Expected a non-empty value for `tunnel_id` but received {tunnel_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["mcp-tunnels-2026-06-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "mcp-tunnels-2026-06-22", **(extra_headers or {})} return self._get_api_list( path_template("/v1/tunnels/{tunnel_id}/certificates?beta=true", tunnel_id=tunnel_id), page=SyncPageCursor[BetaTunnelCertificate], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "include_archived": include_archived, "limit": limit, "page": page, }, certificate_list_params.CertificateListParams, ), ), model=BetaTunnelCertificate, ) def archive( self, certificate_id: str, *, tunnel_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaTunnelCertificate: """The Tunnels API is in research preview. It requires the `anthropic-beta: mcp-tunnels-2026-06-22` header and may change without a deprecation period. It supersedes the Admin API endpoints at `/v1/organizations/tunnels`, which remain available during a migration window. Archives a tunnel certificate, removing it from the set Anthropic trusts for the tunnel. The certificate record is retained. Archiving the last non-archived certificate is permitted; the tunnel rejects MCP traffic until a new certificate is added. Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not tunnel_id: raise ValueError(f"Expected a non-empty value for `tunnel_id` but received {tunnel_id!r}") if not certificate_id: raise ValueError(f"Expected a non-empty value for `certificate_id` but received {certificate_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["mcp-tunnels-2026-06-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "mcp-tunnels-2026-06-22", **(extra_headers or {})} return self._post( path_template( "/v1/tunnels/{tunnel_id}/certificates/{certificate_id}/archive?beta=true", tunnel_id=tunnel_id, certificate_id=certificate_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaTunnelCertificate, ) class AsyncCertificates(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncCertificatesWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncCertificatesWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncCertificatesWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncCertificatesWithStreamingResponse(self) async def create( self, tunnel_id: str, *, ca_certificate_pem: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaTunnelCertificate: """The Tunnels API is in research preview. It requires the `anthropic-beta: mcp-tunnels-2026-06-22` header and may change without a deprecation period. It supersedes the Admin API endpoints at `/v1/organizations/tunnels`, which remain available during a migration window. Registers a public CA certificate on a tunnel. Anthropic verifies the gateway's server certificate against this CA when it terminates the inner TLS session. A tunnel holds at most two non-archived certificates. Args: ca_certificate_pem: PEM-encoded X.509 CA certificate. Must contain exactly one certificate and no private-key material. Maximum 8KB. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not tunnel_id: raise ValueError(f"Expected a non-empty value for `tunnel_id` but received {tunnel_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["mcp-tunnels-2026-06-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "mcp-tunnels-2026-06-22", **(extra_headers or {})} return await self._post( path_template("/v1/tunnels/{tunnel_id}/certificates?beta=true", tunnel_id=tunnel_id), body=await async_maybe_transform( {"ca_certificate_pem": ca_certificate_pem}, certificate_create_params.CertificateCreateParams ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaTunnelCertificate, ) async def retrieve( self, certificate_id: str, *, tunnel_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaTunnelCertificate: """The Tunnels API is in research preview. It requires the `anthropic-beta: mcp-tunnels-2026-06-22` header and may change without a deprecation period. It supersedes the Admin API endpoints at `/v1/organizations/tunnels`, which remain available during a migration window. Fetches a tunnel certificate by ID. Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not tunnel_id: raise ValueError(f"Expected a non-empty value for `tunnel_id` but received {tunnel_id!r}") if not certificate_id: raise ValueError(f"Expected a non-empty value for `certificate_id` but received {certificate_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["mcp-tunnels-2026-06-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "mcp-tunnels-2026-06-22", **(extra_headers or {})} return await self._get( path_template( "/v1/tunnels/{tunnel_id}/certificates/{certificate_id}?beta=true", tunnel_id=tunnel_id, certificate_id=certificate_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaTunnelCertificate, ) def list( self, tunnel_id: str, *, include_archived: bool | Omit = omit, limit: int | Omit = omit, page: str | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[BetaTunnelCertificate, AsyncPageCursor[BetaTunnelCertificate]]: """The Tunnels API is in research preview. It requires the `anthropic-beta: mcp-tunnels-2026-06-22` header and may change without a deprecation period. It supersedes the Admin API endpoints at `/v1/organizations/tunnels`, which remain available during a migration window. Lists the certificates registered on a tunnel. Archived certificates are excluded unless include_archived is set. Args: include_archived: Whether to include archived certificates in the results. Defaults to false. limit: Maximum number of certificates to return per page. Defaults to 20, maximum 1000. page: Opaque pagination cursor from a previous `list_tunnel_certificates` response. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not tunnel_id: raise ValueError(f"Expected a non-empty value for `tunnel_id` but received {tunnel_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["mcp-tunnels-2026-06-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "mcp-tunnels-2026-06-22", **(extra_headers or {})} return self._get_api_list( path_template("/v1/tunnels/{tunnel_id}/certificates?beta=true", tunnel_id=tunnel_id), page=AsyncPageCursor[BetaTunnelCertificate], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "include_archived": include_archived, "limit": limit, "page": page, }, certificate_list_params.CertificateListParams, ), ), model=BetaTunnelCertificate, ) async def archive( self, certificate_id: str, *, tunnel_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaTunnelCertificate: """The Tunnels API is in research preview. It requires the `anthropic-beta: mcp-tunnels-2026-06-22` header and may change without a deprecation period. It supersedes the Admin API endpoints at `/v1/organizations/tunnels`, which remain available during a migration window. Archives a tunnel certificate, removing it from the set Anthropic trusts for the tunnel. The certificate record is retained. Archiving the last non-archived certificate is permitted; the tunnel rejects MCP traffic until a new certificate is added. Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not tunnel_id: raise ValueError(f"Expected a non-empty value for `tunnel_id` but received {tunnel_id!r}") if not certificate_id: raise ValueError(f"Expected a non-empty value for `certificate_id` but received {certificate_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["mcp-tunnels-2026-06-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "mcp-tunnels-2026-06-22", **(extra_headers or {})} return await self._post( path_template( "/v1/tunnels/{tunnel_id}/certificates/{certificate_id}/archive?beta=true", tunnel_id=tunnel_id, certificate_id=certificate_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaTunnelCertificate, ) class CertificatesWithRawResponse: def __init__(self, certificates: Certificates) -> None: self._certificates = certificates self.create = _legacy_response.to_raw_response_wrapper( certificates.create, ) self.retrieve = _legacy_response.to_raw_response_wrapper( certificates.retrieve, ) self.list = _legacy_response.to_raw_response_wrapper( certificates.list, ) self.archive = _legacy_response.to_raw_response_wrapper( certificates.archive, ) class AsyncCertificatesWithRawResponse: def __init__(self, certificates: AsyncCertificates) -> None: self._certificates = certificates self.create = _legacy_response.async_to_raw_response_wrapper( certificates.create, ) self.retrieve = _legacy_response.async_to_raw_response_wrapper( certificates.retrieve, ) self.list = _legacy_response.async_to_raw_response_wrapper( certificates.list, ) self.archive = _legacy_response.async_to_raw_response_wrapper( certificates.archive, ) class CertificatesWithStreamingResponse: def __init__(self, certificates: Certificates) -> None: self._certificates = certificates self.create = to_streamed_response_wrapper( certificates.create, ) self.retrieve = to_streamed_response_wrapper( certificates.retrieve, ) self.list = to_streamed_response_wrapper( certificates.list, ) self.archive = to_streamed_response_wrapper( certificates.archive, ) class AsyncCertificatesWithStreamingResponse: def __init__(self, certificates: AsyncCertificates) -> None: self._certificates = certificates self.create = async_to_streamed_response_wrapper( certificates.create, ) self.retrieve = async_to_streamed_response_wrapper( certificates.retrieve, ) self.list = async_to_streamed_response_wrapper( certificates.list, ) self.archive = async_to_streamed_response_wrapper( certificates.archive, ) anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/tunnels/tunnels.py000066400000000000000000001104341523216435200272040ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from itertools import chain import httpx from .... import _legacy_response from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ...._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from .certificates import ( Certificates, AsyncCertificates, CertificatesWithRawResponse, AsyncCertificatesWithRawResponse, CertificatesWithStreamingResponse, AsyncCertificatesWithStreamingResponse, ) from ....pagination import SyncPageCursor, AsyncPageCursor from ....types.beta import tunnel_list_params, tunnel_create_params, tunnel_rotate_token_params from ...._base_client import AsyncPaginator, make_request_options from ....types.beta.beta_tunnel import BetaTunnel from ....types.anthropic_beta_param import AnthropicBetaParam from ....types.beta.beta_tunnel_token import BetaTunnelToken __all__ = ["Tunnels", "AsyncTunnels"] class Tunnels(SyncAPIResource): @cached_property def certificates(self) -> Certificates: return Certificates(self._client) @cached_property def with_raw_response(self) -> TunnelsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return TunnelsWithRawResponse(self) @cached_property def with_streaming_response(self) -> TunnelsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return TunnelsWithStreamingResponse(self) def create( self, *, display_name: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaTunnel: """The Tunnels API is in research preview. It requires the `anthropic-beta: mcp-tunnels-2026-06-22` header and may change without a deprecation period. It supersedes the Admin API endpoints at `/v1/organizations/tunnels`, which remain available during a migration window. Creates a tunnel. Creation allocates a fresh hostname and provisions the tunnel; it is not idempotent. The new tunnel rejects MCP traffic until at least one CA certificate is added. Args: display_name: Optional human-readable name for the tunnel (1-255 characters). betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["mcp-tunnels-2026-06-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "mcp-tunnels-2026-06-22", **(extra_headers or {})} return self._post( "/v1/tunnels?beta=true", body=maybe_transform({"display_name": display_name}, tunnel_create_params.TunnelCreateParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaTunnel, ) def retrieve( self, tunnel_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaTunnel: """The Tunnels API is in research preview. It requires the `anthropic-beta: mcp-tunnels-2026-06-22` header and may change without a deprecation period. It supersedes the Admin API endpoints at `/v1/organizations/tunnels`, which remain available during a migration window. Fetches a tunnel by ID. Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not tunnel_id: raise ValueError(f"Expected a non-empty value for `tunnel_id` but received {tunnel_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["mcp-tunnels-2026-06-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "mcp-tunnels-2026-06-22", **(extra_headers or {})} return self._get( path_template("/v1/tunnels/{tunnel_id}?beta=true", tunnel_id=tunnel_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaTunnel, ) def list( self, *, include_archived: bool | Omit = omit, limit: int | Omit = omit, page: str | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncPageCursor[BetaTunnel]: """The Tunnels API is in research preview. It requires the `anthropic-beta: mcp-tunnels-2026-06-22` header and may change without a deprecation period. It supersedes the Admin API endpoints at `/v1/organizations/tunnels`, which remain available during a migration window. Lists tunnels. Results are ordered by creation time, newest first; archived tunnels are excluded unless include_archived is set. Args: include_archived: Whether to include archived tunnels in the results. Defaults to false. limit: Maximum number of tunnels to return per page. Defaults to 20, maximum 1000. page: Opaque pagination cursor from a previous `list_tunnels` response. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["mcp-tunnels-2026-06-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "mcp-tunnels-2026-06-22", **(extra_headers or {})} return self._get_api_list( "/v1/tunnels?beta=true", page=SyncPageCursor[BetaTunnel], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "include_archived": include_archived, "limit": limit, "page": page, }, tunnel_list_params.TunnelListParams, ), ), model=BetaTunnel, ) def archive( self, tunnel_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaTunnel: """The Tunnels API is in research preview. It requires the `anthropic-beta: mcp-tunnels-2026-06-22` header and may change without a deprecation period. It supersedes the Admin API endpoints at `/v1/organizations/tunnels`, which remain available during a migration window. Archives a tunnel. Archival is irreversible: every non-archived certificate on the tunnel is archived in the same operation, the hostname is retired and never re-allocated, and the tunnel token is invalidated. Retrying against an already-archived tunnel returns the existing record unchanged. Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not tunnel_id: raise ValueError(f"Expected a non-empty value for `tunnel_id` but received {tunnel_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["mcp-tunnels-2026-06-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "mcp-tunnels-2026-06-22", **(extra_headers or {})} return self._post( path_template("/v1/tunnels/{tunnel_id}/archive?beta=true", tunnel_id=tunnel_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaTunnel, ) def reveal_token( self, tunnel_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaTunnelToken: """The Tunnels API is in research preview. It requires the `anthropic-beta: mcp-tunnels-2026-06-22` header and may change without a deprecation period. It supersedes the Admin API endpoints at `/v1/organizations/tunnels`, which remain available during a migration window. Reveals a tunnel's connector token. The value is fetched live on each call; Anthropic does not store it. Repeated calls return the same value until the token is rotated. Exposed as POST so the token does not appear in intermediary access logs. Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not tunnel_id: raise ValueError(f"Expected a non-empty value for `tunnel_id` but received {tunnel_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["mcp-tunnels-2026-06-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "mcp-tunnels-2026-06-22", **(extra_headers or {})} return self._post( path_template("/v1/tunnels/{tunnel_id}/reveal_token?beta=true", tunnel_id=tunnel_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaTunnelToken, ) def rotate_token( self, tunnel_id: str, *, reason: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaTunnelToken: """The Tunnels API is in research preview. It requires the `anthropic-beta: mcp-tunnels-2026-06-22` header and may change without a deprecation period. It supersedes the Admin API endpoints at `/v1/organizations/tunnels`, which remain available during a migration window. Rotates a tunnel's connector token. Rotation invalidates the current token for new connections and returns a fresh value; established connections are not severed. A connector restarted after rotation must use the new value. Args: reason: Optional free-text reason for the rotation, recorded for audit. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not tunnel_id: raise ValueError(f"Expected a non-empty value for `tunnel_id` but received {tunnel_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["mcp-tunnels-2026-06-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "mcp-tunnels-2026-06-22", **(extra_headers or {})} return self._post( path_template("/v1/tunnels/{tunnel_id}/rotate_token?beta=true", tunnel_id=tunnel_id), body=maybe_transform({"reason": reason}, tunnel_rotate_token_params.TunnelRotateTokenParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaTunnelToken, ) class AsyncTunnels(AsyncAPIResource): @cached_property def certificates(self) -> AsyncCertificates: return AsyncCertificates(self._client) @cached_property def with_raw_response(self) -> AsyncTunnelsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncTunnelsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncTunnelsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncTunnelsWithStreamingResponse(self) async def create( self, *, display_name: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaTunnel: """The Tunnels API is in research preview. It requires the `anthropic-beta: mcp-tunnels-2026-06-22` header and may change without a deprecation period. It supersedes the Admin API endpoints at `/v1/organizations/tunnels`, which remain available during a migration window. Creates a tunnel. Creation allocates a fresh hostname and provisions the tunnel; it is not idempotent. The new tunnel rejects MCP traffic until at least one CA certificate is added. Args: display_name: Optional human-readable name for the tunnel (1-255 characters). betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["mcp-tunnels-2026-06-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "mcp-tunnels-2026-06-22", **(extra_headers or {})} return await self._post( "/v1/tunnels?beta=true", body=await async_maybe_transform({"display_name": display_name}, tunnel_create_params.TunnelCreateParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaTunnel, ) async def retrieve( self, tunnel_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaTunnel: """The Tunnels API is in research preview. It requires the `anthropic-beta: mcp-tunnels-2026-06-22` header and may change without a deprecation period. It supersedes the Admin API endpoints at `/v1/organizations/tunnels`, which remain available during a migration window. Fetches a tunnel by ID. Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not tunnel_id: raise ValueError(f"Expected a non-empty value for `tunnel_id` but received {tunnel_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["mcp-tunnels-2026-06-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "mcp-tunnels-2026-06-22", **(extra_headers or {})} return await self._get( path_template("/v1/tunnels/{tunnel_id}?beta=true", tunnel_id=tunnel_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaTunnel, ) def list( self, *, include_archived: bool | Omit = omit, limit: int | Omit = omit, page: str | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[BetaTunnel, AsyncPageCursor[BetaTunnel]]: """The Tunnels API is in research preview. It requires the `anthropic-beta: mcp-tunnels-2026-06-22` header and may change without a deprecation period. It supersedes the Admin API endpoints at `/v1/organizations/tunnels`, which remain available during a migration window. Lists tunnels. Results are ordered by creation time, newest first; archived tunnels are excluded unless include_archived is set. Args: include_archived: Whether to include archived tunnels in the results. Defaults to false. limit: Maximum number of tunnels to return per page. Defaults to 20, maximum 1000. page: Opaque pagination cursor from a previous `list_tunnels` response. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["mcp-tunnels-2026-06-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "mcp-tunnels-2026-06-22", **(extra_headers or {})} return self._get_api_list( "/v1/tunnels?beta=true", page=AsyncPageCursor[BetaTunnel], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "include_archived": include_archived, "limit": limit, "page": page, }, tunnel_list_params.TunnelListParams, ), ), model=BetaTunnel, ) async def archive( self, tunnel_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaTunnel: """The Tunnels API is in research preview. It requires the `anthropic-beta: mcp-tunnels-2026-06-22` header and may change without a deprecation period. It supersedes the Admin API endpoints at `/v1/organizations/tunnels`, which remain available during a migration window. Archives a tunnel. Archival is irreversible: every non-archived certificate on the tunnel is archived in the same operation, the hostname is retired and never re-allocated, and the tunnel token is invalidated. Retrying against an already-archived tunnel returns the existing record unchanged. Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not tunnel_id: raise ValueError(f"Expected a non-empty value for `tunnel_id` but received {tunnel_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["mcp-tunnels-2026-06-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "mcp-tunnels-2026-06-22", **(extra_headers or {})} return await self._post( path_template("/v1/tunnels/{tunnel_id}/archive?beta=true", tunnel_id=tunnel_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaTunnel, ) async def reveal_token( self, tunnel_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaTunnelToken: """The Tunnels API is in research preview. It requires the `anthropic-beta: mcp-tunnels-2026-06-22` header and may change without a deprecation period. It supersedes the Admin API endpoints at `/v1/organizations/tunnels`, which remain available during a migration window. Reveals a tunnel's connector token. The value is fetched live on each call; Anthropic does not store it. Repeated calls return the same value until the token is rotated. Exposed as POST so the token does not appear in intermediary access logs. Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not tunnel_id: raise ValueError(f"Expected a non-empty value for `tunnel_id` but received {tunnel_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["mcp-tunnels-2026-06-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "mcp-tunnels-2026-06-22", **(extra_headers or {})} return await self._post( path_template("/v1/tunnels/{tunnel_id}/reveal_token?beta=true", tunnel_id=tunnel_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaTunnelToken, ) async def rotate_token( self, tunnel_id: str, *, reason: Optional[str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaTunnelToken: """The Tunnels API is in research preview. It requires the `anthropic-beta: mcp-tunnels-2026-06-22` header and may change without a deprecation period. It supersedes the Admin API endpoints at `/v1/organizations/tunnels`, which remain available during a migration window. Rotates a tunnel's connector token. Rotation invalidates the current token for new connections and returns a fresh value; established connections are not severed. A connector restarted after rotation must use the new value. Args: reason: Optional free-text reason for the rotation, recorded for audit. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not tunnel_id: raise ValueError(f"Expected a non-empty value for `tunnel_id` but received {tunnel_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["mcp-tunnels-2026-06-22"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "mcp-tunnels-2026-06-22", **(extra_headers or {})} return await self._post( path_template("/v1/tunnels/{tunnel_id}/rotate_token?beta=true", tunnel_id=tunnel_id), body=await async_maybe_transform({"reason": reason}, tunnel_rotate_token_params.TunnelRotateTokenParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaTunnelToken, ) class TunnelsWithRawResponse: def __init__(self, tunnels: Tunnels) -> None: self._tunnels = tunnels self.create = _legacy_response.to_raw_response_wrapper( tunnels.create, ) self.retrieve = _legacy_response.to_raw_response_wrapper( tunnels.retrieve, ) self.list = _legacy_response.to_raw_response_wrapper( tunnels.list, ) self.archive = _legacy_response.to_raw_response_wrapper( tunnels.archive, ) self.reveal_token = _legacy_response.to_raw_response_wrapper( tunnels.reveal_token, ) self.rotate_token = _legacy_response.to_raw_response_wrapper( tunnels.rotate_token, ) @cached_property def certificates(self) -> CertificatesWithRawResponse: return CertificatesWithRawResponse(self._tunnels.certificates) class AsyncTunnelsWithRawResponse: def __init__(self, tunnels: AsyncTunnels) -> None: self._tunnels = tunnels self.create = _legacy_response.async_to_raw_response_wrapper( tunnels.create, ) self.retrieve = _legacy_response.async_to_raw_response_wrapper( tunnels.retrieve, ) self.list = _legacy_response.async_to_raw_response_wrapper( tunnels.list, ) self.archive = _legacy_response.async_to_raw_response_wrapper( tunnels.archive, ) self.reveal_token = _legacy_response.async_to_raw_response_wrapper( tunnels.reveal_token, ) self.rotate_token = _legacy_response.async_to_raw_response_wrapper( tunnels.rotate_token, ) @cached_property def certificates(self) -> AsyncCertificatesWithRawResponse: return AsyncCertificatesWithRawResponse(self._tunnels.certificates) class TunnelsWithStreamingResponse: def __init__(self, tunnels: Tunnels) -> None: self._tunnels = tunnels self.create = to_streamed_response_wrapper( tunnels.create, ) self.retrieve = to_streamed_response_wrapper( tunnels.retrieve, ) self.list = to_streamed_response_wrapper( tunnels.list, ) self.archive = to_streamed_response_wrapper( tunnels.archive, ) self.reveal_token = to_streamed_response_wrapper( tunnels.reveal_token, ) self.rotate_token = to_streamed_response_wrapper( tunnels.rotate_token, ) @cached_property def certificates(self) -> CertificatesWithStreamingResponse: return CertificatesWithStreamingResponse(self._tunnels.certificates) class AsyncTunnelsWithStreamingResponse: def __init__(self, tunnels: AsyncTunnels) -> None: self._tunnels = tunnels self.create = async_to_streamed_response_wrapper( tunnels.create, ) self.retrieve = async_to_streamed_response_wrapper( tunnels.retrieve, ) self.list = async_to_streamed_response_wrapper( tunnels.list, ) self.archive = async_to_streamed_response_wrapper( tunnels.archive, ) self.reveal_token = async_to_streamed_response_wrapper( tunnels.reveal_token, ) self.rotate_token = async_to_streamed_response_wrapper( tunnels.rotate_token, ) @cached_property def certificates(self) -> AsyncCertificatesWithStreamingResponse: return AsyncCertificatesWithStreamingResponse(self._tunnels.certificates) anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/user_profiles.py000066400000000000000000000752051523216435200267130ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Optional from itertools import chain from typing_extensions import Literal import httpx from ... import _legacy_response from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ..._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ...pagination import SyncPageCursor, AsyncPageCursor from ...types.beta import user_profile_list_params, user_profile_create_params, user_profile_update_params from ..._base_client import AsyncPaginator, make_request_options from ...types.anthropic_beta_param import AnthropicBetaParam from ...types.beta.beta_user_profile import BetaUserProfile from ...types.beta.beta_user_profile_enrollment_url import BetaUserProfileEnrollmentURL __all__ = ["UserProfiles", "AsyncUserProfiles"] class UserProfiles(SyncAPIResource): @cached_property def with_raw_response(self) -> UserProfilesWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return UserProfilesWithRawResponse(self) @cached_property def with_streaming_response(self) -> UserProfilesWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return UserProfilesWithStreamingResponse(self) def create( self, *, external_id: Optional[str] | Omit = omit, metadata: Dict[str, str] | Omit = omit, name: Optional[str] | Omit = omit, relationship: Literal["external", "resold", "internal"] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaUserProfile: """ Create User Profile Args: external_id: Platform's own identifier for this user. Not enforced unique. Maximum 255 characters. metadata: Free-form key-value data to attach to this user profile. Maximum 16 keys, with keys up to 64 characters and values up to 512 characters. Values must be non-empty strings. name: Display name of the entity this profile represents. Required when relationship is `resold` (the resold-to company's name); optional otherwise. Maximum 255 characters. relationship: How the entity behind a user profile relates to the platform that owns the API key. `external`: an individual end-user of the platform. `resold`: a company the platform resells Claude access to. `internal`: the platform's own usage. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["user-profiles-2026-03-24"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "user-profiles-2026-03-24", **(extra_headers or {})} return self._post( "/v1/user_profiles?beta=true", body=maybe_transform( { "external_id": external_id, "metadata": metadata, "name": name, "relationship": relationship, }, user_profile_create_params.UserProfileCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaUserProfile, ) def retrieve( self, user_profile_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaUserProfile: """ Get User Profile Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not user_profile_id: raise ValueError(f"Expected a non-empty value for `user_profile_id` but received {user_profile_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["user-profiles-2026-03-24"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "user-profiles-2026-03-24", **(extra_headers or {})} return self._get( path_template("/v1/user_profiles/{user_profile_id}?beta=true", user_profile_id=user_profile_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaUserProfile, ) def update( self, user_profile_id: str, *, external_id: Optional[str] | Omit = omit, metadata: Dict[str, str] | Omit = omit, name: Optional[str] | Omit = omit, relationship: Optional[Literal["external", "resold", "internal"]] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaUserProfile: """ Update User Profile Args: external_id: If present, replaces the stored external_id. Omit to leave unchanged. Maximum 255 characters. metadata: Key-value pairs to merge into the stored metadata. Keys provided overwrite existing values. To remove a key, set its value to an empty string. Keys not provided are left unchanged. Maximum 16 keys, with keys up to 64 characters and values up to 512 characters. name: If present, replaces the stored name. Omit to leave unchanged. Maximum 255 characters. relationship: How the entity behind a user profile relates to the platform that owns the API key. `external`: an individual end-user of the platform. `resold`: a company the platform resells Claude access to. `internal`: the platform's own usage. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not user_profile_id: raise ValueError(f"Expected a non-empty value for `user_profile_id` but received {user_profile_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["user-profiles-2026-03-24"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "user-profiles-2026-03-24", **(extra_headers or {})} return self._post( path_template("/v1/user_profiles/{user_profile_id}?beta=true", user_profile_id=user_profile_id), body=maybe_transform( { "external_id": external_id, "metadata": metadata, "name": name, "relationship": relationship, }, user_profile_update_params.UserProfileUpdateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaUserProfile, ) def list( self, *, limit: int | Omit = omit, order: Literal["asc", "desc"] | Omit = omit, page: str | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncPageCursor[BetaUserProfile]: """ List User Profiles Args: limit: Query parameter for limit order: Query parameter for order page: Query parameter for page betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["user-profiles-2026-03-24"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "user-profiles-2026-03-24", **(extra_headers or {})} return self._get_api_list( "/v1/user_profiles?beta=true", page=SyncPageCursor[BetaUserProfile], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "limit": limit, "order": order, "page": page, }, user_profile_list_params.UserProfileListParams, ), ), model=BetaUserProfile, ) def create_enrollment_url( self, user_profile_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaUserProfileEnrollmentURL: """ Create Enrollment URL Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not user_profile_id: raise ValueError(f"Expected a non-empty value for `user_profile_id` but received {user_profile_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["user-profiles-2026-03-24"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "user-profiles-2026-03-24", **(extra_headers or {})} return self._post( path_template( "/v1/user_profiles/{user_profile_id}/enrollment_url?beta=true", user_profile_id=user_profile_id ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaUserProfileEnrollmentURL, ) class AsyncUserProfiles(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncUserProfilesWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncUserProfilesWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncUserProfilesWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncUserProfilesWithStreamingResponse(self) async def create( self, *, external_id: Optional[str] | Omit = omit, metadata: Dict[str, str] | Omit = omit, name: Optional[str] | Omit = omit, relationship: Literal["external", "resold", "internal"] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaUserProfile: """ Create User Profile Args: external_id: Platform's own identifier for this user. Not enforced unique. Maximum 255 characters. metadata: Free-form key-value data to attach to this user profile. Maximum 16 keys, with keys up to 64 characters and values up to 512 characters. Values must be non-empty strings. name: Display name of the entity this profile represents. Required when relationship is `resold` (the resold-to company's name); optional otherwise. Maximum 255 characters. relationship: How the entity behind a user profile relates to the platform that owns the API key. `external`: an individual end-user of the platform. `resold`: a company the platform resells Claude access to. `internal`: the platform's own usage. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["user-profiles-2026-03-24"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "user-profiles-2026-03-24", **(extra_headers or {})} return await self._post( "/v1/user_profiles?beta=true", body=await async_maybe_transform( { "external_id": external_id, "metadata": metadata, "name": name, "relationship": relationship, }, user_profile_create_params.UserProfileCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaUserProfile, ) async def retrieve( self, user_profile_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaUserProfile: """ Get User Profile Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not user_profile_id: raise ValueError(f"Expected a non-empty value for `user_profile_id` but received {user_profile_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["user-profiles-2026-03-24"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "user-profiles-2026-03-24", **(extra_headers or {})} return await self._get( path_template("/v1/user_profiles/{user_profile_id}?beta=true", user_profile_id=user_profile_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaUserProfile, ) async def update( self, user_profile_id: str, *, external_id: Optional[str] | Omit = omit, metadata: Dict[str, str] | Omit = omit, name: Optional[str] | Omit = omit, relationship: Optional[Literal["external", "resold", "internal"]] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaUserProfile: """ Update User Profile Args: external_id: If present, replaces the stored external_id. Omit to leave unchanged. Maximum 255 characters. metadata: Key-value pairs to merge into the stored metadata. Keys provided overwrite existing values. To remove a key, set its value to an empty string. Keys not provided are left unchanged. Maximum 16 keys, with keys up to 64 characters and values up to 512 characters. name: If present, replaces the stored name. Omit to leave unchanged. Maximum 255 characters. relationship: How the entity behind a user profile relates to the platform that owns the API key. `external`: an individual end-user of the platform. `resold`: a company the platform resells Claude access to. `internal`: the platform's own usage. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not user_profile_id: raise ValueError(f"Expected a non-empty value for `user_profile_id` but received {user_profile_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["user-profiles-2026-03-24"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "user-profiles-2026-03-24", **(extra_headers or {})} return await self._post( path_template("/v1/user_profiles/{user_profile_id}?beta=true", user_profile_id=user_profile_id), body=await async_maybe_transform( { "external_id": external_id, "metadata": metadata, "name": name, "relationship": relationship, }, user_profile_update_params.UserProfileUpdateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaUserProfile, ) def list( self, *, limit: int | Omit = omit, order: Literal["asc", "desc"] | Omit = omit, page: str | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[BetaUserProfile, AsyncPageCursor[BetaUserProfile]]: """ List User Profiles Args: limit: Query parameter for limit order: Query parameter for order page: Query parameter for page betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["user-profiles-2026-03-24"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "user-profiles-2026-03-24", **(extra_headers or {})} return self._get_api_list( "/v1/user_profiles?beta=true", page=AsyncPageCursor[BetaUserProfile], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "limit": limit, "order": order, "page": page, }, user_profile_list_params.UserProfileListParams, ), ), model=BetaUserProfile, ) async def create_enrollment_url( self, user_profile_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaUserProfileEnrollmentURL: """ Create Enrollment URL Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not user_profile_id: raise ValueError(f"Expected a non-empty value for `user_profile_id` but received {user_profile_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["user-profiles-2026-03-24"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "user-profiles-2026-03-24", **(extra_headers or {})} return await self._post( path_template( "/v1/user_profiles/{user_profile_id}/enrollment_url?beta=true", user_profile_id=user_profile_id ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaUserProfileEnrollmentURL, ) class UserProfilesWithRawResponse: def __init__(self, user_profiles: UserProfiles) -> None: self._user_profiles = user_profiles self.create = _legacy_response.to_raw_response_wrapper( user_profiles.create, ) self.retrieve = _legacy_response.to_raw_response_wrapper( user_profiles.retrieve, ) self.update = _legacy_response.to_raw_response_wrapper( user_profiles.update, ) self.list = _legacy_response.to_raw_response_wrapper( user_profiles.list, ) self.create_enrollment_url = _legacy_response.to_raw_response_wrapper( user_profiles.create_enrollment_url, ) class AsyncUserProfilesWithRawResponse: def __init__(self, user_profiles: AsyncUserProfiles) -> None: self._user_profiles = user_profiles self.create = _legacy_response.async_to_raw_response_wrapper( user_profiles.create, ) self.retrieve = _legacy_response.async_to_raw_response_wrapper( user_profiles.retrieve, ) self.update = _legacy_response.async_to_raw_response_wrapper( user_profiles.update, ) self.list = _legacy_response.async_to_raw_response_wrapper( user_profiles.list, ) self.create_enrollment_url = _legacy_response.async_to_raw_response_wrapper( user_profiles.create_enrollment_url, ) class UserProfilesWithStreamingResponse: def __init__(self, user_profiles: UserProfiles) -> None: self._user_profiles = user_profiles self.create = to_streamed_response_wrapper( user_profiles.create, ) self.retrieve = to_streamed_response_wrapper( user_profiles.retrieve, ) self.update = to_streamed_response_wrapper( user_profiles.update, ) self.list = to_streamed_response_wrapper( user_profiles.list, ) self.create_enrollment_url = to_streamed_response_wrapper( user_profiles.create_enrollment_url, ) class AsyncUserProfilesWithStreamingResponse: def __init__(self, user_profiles: AsyncUserProfiles) -> None: self._user_profiles = user_profiles self.create = async_to_streamed_response_wrapper( user_profiles.create, ) self.retrieve = async_to_streamed_response_wrapper( user_profiles.retrieve, ) self.update = async_to_streamed_response_wrapper( user_profiles.update, ) self.list = async_to_streamed_response_wrapper( user_profiles.list, ) self.create_enrollment_url = async_to_streamed_response_wrapper( user_profiles.create_enrollment_url, ) anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/vaults/000077500000000000000000000000001523216435200247655ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/vaults/__init__.py000066400000000000000000000015531523216435200271020ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .vaults import ( Vaults, AsyncVaults, VaultsWithRawResponse, AsyncVaultsWithRawResponse, VaultsWithStreamingResponse, AsyncVaultsWithStreamingResponse, ) from .credentials import ( Credentials, AsyncCredentials, CredentialsWithRawResponse, AsyncCredentialsWithRawResponse, CredentialsWithStreamingResponse, AsyncCredentialsWithStreamingResponse, ) __all__ = [ "Credentials", "AsyncCredentials", "CredentialsWithRawResponse", "AsyncCredentialsWithRawResponse", "CredentialsWithStreamingResponse", "AsyncCredentialsWithStreamingResponse", "Vaults", "AsyncVaults", "VaultsWithRawResponse", "AsyncVaultsWithRawResponse", "VaultsWithStreamingResponse", "AsyncVaultsWithStreamingResponse", ] anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/vaults/credentials.py000066400000000000000000001203071523216435200276370ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Optional from itertools import chain import httpx from .... import _legacy_response from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ...._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ....pagination import SyncPageCursor, AsyncPageCursor from ...._base_client import AsyncPaginator, make_request_options from ....types.beta.vaults import credential_list_params, credential_create_params, credential_update_params from ....types.anthropic_beta_param import AnthropicBetaParam from ....types.beta.vaults.beta_managed_agents_credential import BetaManagedAgentsCredential from ....types.beta.vaults.beta_managed_agents_deleted_credential import BetaManagedAgentsDeletedCredential from ....types.beta.vaults.beta_managed_agents_credential_validation import BetaManagedAgentsCredentialValidation __all__ = ["Credentials", "AsyncCredentials"] class Credentials(SyncAPIResource): @cached_property def with_raw_response(self) -> CredentialsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return CredentialsWithRawResponse(self) @cached_property def with_streaming_response(self) -> CredentialsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return CredentialsWithStreamingResponse(self) def create( self, vault_id: str, *, auth: credential_create_params.Auth, display_name: Optional[str] | Omit = omit, metadata: Dict[str, str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsCredential: """ Create Credential Args: auth: Authentication details for creating a credential. display_name: Human-readable name for the credential. Up to 255 characters. metadata: Arbitrary key-value metadata to attach to the credential. Maximum 16 pairs, keys up to 64 chars, values up to 512 chars. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not vault_id: raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._post( path_template("/v1/vaults/{vault_id}/credentials?beta=true", vault_id=vault_id), body=maybe_transform( { "auth": auth, "display_name": display_name, "metadata": metadata, }, credential_create_params.CredentialCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsCredential, ) def retrieve( self, credential_id: str, *, vault_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsCredential: """ Get Credential Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not vault_id: raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") if not credential_id: raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get( path_template( "/v1/vaults/{vault_id}/credentials/{credential_id}?beta=true", vault_id=vault_id, credential_id=credential_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsCredential, ) def update( self, credential_id: str, *, vault_id: str, auth: credential_update_params.Auth | Omit = omit, display_name: Optional[str] | Omit = omit, metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsCredential: """ Update Credential Args: auth: Updated authentication details for a credential. display_name: Updated human-readable name for the credential. 1-255 characters. metadata: Metadata patch. Set a key to a string to upsert it, or to null to delete it. Omitted keys are preserved. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not vault_id: raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") if not credential_id: raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._post( path_template( "/v1/vaults/{vault_id}/credentials/{credential_id}?beta=true", vault_id=vault_id, credential_id=credential_id, ), body=maybe_transform( { "auth": auth, "display_name": display_name, "metadata": metadata, }, credential_update_params.CredentialUpdateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsCredential, ) def list( self, vault_id: str, *, include_archived: bool | Omit = omit, limit: int | Omit = omit, page: str | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncPageCursor[BetaManagedAgentsCredential]: """ List Credentials Args: include_archived: Whether to include archived credentials in the results. limit: Maximum number of credentials to return per page. Defaults to 20, maximum 100. page: Opaque pagination token from a previous `list_credentials` response. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not vault_id: raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get_api_list( path_template("/v1/vaults/{vault_id}/credentials?beta=true", vault_id=vault_id), page=SyncPageCursor[BetaManagedAgentsCredential], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "include_archived": include_archived, "limit": limit, "page": page, }, credential_list_params.CredentialListParams, ), ), model=BetaManagedAgentsCredential, ) def delete( self, credential_id: str, *, vault_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsDeletedCredential: """ Delete Credential Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not vault_id: raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") if not credential_id: raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._delete( path_template( "/v1/vaults/{vault_id}/credentials/{credential_id}?beta=true", vault_id=vault_id, credential_id=credential_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsDeletedCredential, ) def archive( self, credential_id: str, *, vault_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsCredential: """ Archive Credential Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not vault_id: raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") if not credential_id: raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._post( path_template( "/v1/vaults/{vault_id}/credentials/{credential_id}/archive?beta=true", vault_id=vault_id, credential_id=credential_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsCredential, ) def mcp_oauth_validate( self, credential_id: str, *, vault_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsCredentialValidation: """ Validate Credential Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not vault_id: raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") if not credential_id: raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._post( path_template( "/v1/vaults/{vault_id}/credentials/{credential_id}/mcp_oauth_validate?beta=true", vault_id=vault_id, credential_id=credential_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsCredentialValidation, ) class AsyncCredentials(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncCredentialsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncCredentialsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncCredentialsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncCredentialsWithStreamingResponse(self) async def create( self, vault_id: str, *, auth: credential_create_params.Auth, display_name: Optional[str] | Omit = omit, metadata: Dict[str, str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsCredential: """ Create Credential Args: auth: Authentication details for creating a credential. display_name: Human-readable name for the credential. Up to 255 characters. metadata: Arbitrary key-value metadata to attach to the credential. Maximum 16 pairs, keys up to 64 chars, values up to 512 chars. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not vault_id: raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._post( path_template("/v1/vaults/{vault_id}/credentials?beta=true", vault_id=vault_id), body=await async_maybe_transform( { "auth": auth, "display_name": display_name, "metadata": metadata, }, credential_create_params.CredentialCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsCredential, ) async def retrieve( self, credential_id: str, *, vault_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsCredential: """ Get Credential Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not vault_id: raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") if not credential_id: raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._get( path_template( "/v1/vaults/{vault_id}/credentials/{credential_id}?beta=true", vault_id=vault_id, credential_id=credential_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsCredential, ) async def update( self, credential_id: str, *, vault_id: str, auth: credential_update_params.Auth | Omit = omit, display_name: Optional[str] | Omit = omit, metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsCredential: """ Update Credential Args: auth: Updated authentication details for a credential. display_name: Updated human-readable name for the credential. 1-255 characters. metadata: Metadata patch. Set a key to a string to upsert it, or to null to delete it. Omitted keys are preserved. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not vault_id: raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") if not credential_id: raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._post( path_template( "/v1/vaults/{vault_id}/credentials/{credential_id}?beta=true", vault_id=vault_id, credential_id=credential_id, ), body=await async_maybe_transform( { "auth": auth, "display_name": display_name, "metadata": metadata, }, credential_update_params.CredentialUpdateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsCredential, ) def list( self, vault_id: str, *, include_archived: bool | Omit = omit, limit: int | Omit = omit, page: str | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[BetaManagedAgentsCredential, AsyncPageCursor[BetaManagedAgentsCredential]]: """ List Credentials Args: include_archived: Whether to include archived credentials in the results. limit: Maximum number of credentials to return per page. Defaults to 20, maximum 100. page: Opaque pagination token from a previous `list_credentials` response. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not vault_id: raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get_api_list( path_template("/v1/vaults/{vault_id}/credentials?beta=true", vault_id=vault_id), page=AsyncPageCursor[BetaManagedAgentsCredential], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "include_archived": include_archived, "limit": limit, "page": page, }, credential_list_params.CredentialListParams, ), ), model=BetaManagedAgentsCredential, ) async def delete( self, credential_id: str, *, vault_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsDeletedCredential: """ Delete Credential Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not vault_id: raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") if not credential_id: raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._delete( path_template( "/v1/vaults/{vault_id}/credentials/{credential_id}?beta=true", vault_id=vault_id, credential_id=credential_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsDeletedCredential, ) async def archive( self, credential_id: str, *, vault_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsCredential: """ Archive Credential Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not vault_id: raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") if not credential_id: raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._post( path_template( "/v1/vaults/{vault_id}/credentials/{credential_id}/archive?beta=true", vault_id=vault_id, credential_id=credential_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsCredential, ) async def mcp_oauth_validate( self, credential_id: str, *, vault_id: str, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsCredentialValidation: """ Validate Credential Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not vault_id: raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") if not credential_id: raise ValueError(f"Expected a non-empty value for `credential_id` but received {credential_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._post( path_template( "/v1/vaults/{vault_id}/credentials/{credential_id}/mcp_oauth_validate?beta=true", vault_id=vault_id, credential_id=credential_id, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsCredentialValidation, ) class CredentialsWithRawResponse: def __init__(self, credentials: Credentials) -> None: self._credentials = credentials self.create = _legacy_response.to_raw_response_wrapper( credentials.create, ) self.retrieve = _legacy_response.to_raw_response_wrapper( credentials.retrieve, ) self.update = _legacy_response.to_raw_response_wrapper( credentials.update, ) self.list = _legacy_response.to_raw_response_wrapper( credentials.list, ) self.delete = _legacy_response.to_raw_response_wrapper( credentials.delete, ) self.archive = _legacy_response.to_raw_response_wrapper( credentials.archive, ) self.mcp_oauth_validate = _legacy_response.to_raw_response_wrapper( credentials.mcp_oauth_validate, ) class AsyncCredentialsWithRawResponse: def __init__(self, credentials: AsyncCredentials) -> None: self._credentials = credentials self.create = _legacy_response.async_to_raw_response_wrapper( credentials.create, ) self.retrieve = _legacy_response.async_to_raw_response_wrapper( credentials.retrieve, ) self.update = _legacy_response.async_to_raw_response_wrapper( credentials.update, ) self.list = _legacy_response.async_to_raw_response_wrapper( credentials.list, ) self.delete = _legacy_response.async_to_raw_response_wrapper( credentials.delete, ) self.archive = _legacy_response.async_to_raw_response_wrapper( credentials.archive, ) self.mcp_oauth_validate = _legacy_response.async_to_raw_response_wrapper( credentials.mcp_oauth_validate, ) class CredentialsWithStreamingResponse: def __init__(self, credentials: Credentials) -> None: self._credentials = credentials self.create = to_streamed_response_wrapper( credentials.create, ) self.retrieve = to_streamed_response_wrapper( credentials.retrieve, ) self.update = to_streamed_response_wrapper( credentials.update, ) self.list = to_streamed_response_wrapper( credentials.list, ) self.delete = to_streamed_response_wrapper( credentials.delete, ) self.archive = to_streamed_response_wrapper( credentials.archive, ) self.mcp_oauth_validate = to_streamed_response_wrapper( credentials.mcp_oauth_validate, ) class AsyncCredentialsWithStreamingResponse: def __init__(self, credentials: AsyncCredentials) -> None: self._credentials = credentials self.create = async_to_streamed_response_wrapper( credentials.create, ) self.retrieve = async_to_streamed_response_wrapper( credentials.retrieve, ) self.update = async_to_streamed_response_wrapper( credentials.update, ) self.list = async_to_streamed_response_wrapper( credentials.list, ) self.delete = async_to_streamed_response_wrapper( credentials.delete, ) self.archive = async_to_streamed_response_wrapper( credentials.archive, ) self.mcp_oauth_validate = async_to_streamed_response_wrapper( credentials.mcp_oauth_validate, ) anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/vaults/vaults.py000066400000000000000000000775721523216435200266770ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Optional from itertools import chain import httpx from .... import _legacy_response from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ...._utils import is_given, path_template, maybe_transform, strip_not_given, async_maybe_transform from ...._compat import cached_property from .credentials import ( Credentials, AsyncCredentials, CredentialsWithRawResponse, AsyncCredentialsWithRawResponse, CredentialsWithStreamingResponse, AsyncCredentialsWithStreamingResponse, ) from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ....pagination import SyncPageCursor, AsyncPageCursor from ....types.beta import vault_list_params, vault_create_params, vault_update_params from ...._base_client import AsyncPaginator, make_request_options from ....types.anthropic_beta_param import AnthropicBetaParam from ....types.beta.beta_managed_agents_vault import BetaManagedAgentsVault from ....types.beta.beta_managed_agents_deleted_vault import BetaManagedAgentsDeletedVault __all__ = ["Vaults", "AsyncVaults"] class Vaults(SyncAPIResource): @cached_property def credentials(self) -> Credentials: return Credentials(self._client) @cached_property def with_raw_response(self) -> VaultsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return VaultsWithRawResponse(self) @cached_property def with_streaming_response(self) -> VaultsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return VaultsWithStreamingResponse(self) def create( self, *, display_name: str, metadata: Dict[str, str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsVault: """Create Vault Args: display_name: Human-readable name for the vault. 1-255 characters. metadata: Arbitrary key-value metadata to attach to the vault. Maximum 16 pairs, keys up to 64 chars, values up to 512 chars. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._post( "/v1/vaults?beta=true", body=maybe_transform( { "display_name": display_name, "metadata": metadata, }, vault_create_params.VaultCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsVault, ) def retrieve( self, vault_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsVault: """ Get Vault Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not vault_id: raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get( path_template("/v1/vaults/{vault_id}?beta=true", vault_id=vault_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsVault, ) def update( self, vault_id: str, *, display_name: Optional[str] | Omit = omit, metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsVault: """Update Vault Args: display_name: Updated human-readable name for the vault. 1-255 characters. metadata: Metadata patch. Set a key to a string to upsert it, or to null to delete it. Omitted keys are preserved. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not vault_id: raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._post( path_template("/v1/vaults/{vault_id}?beta=true", vault_id=vault_id), body=maybe_transform( { "display_name": display_name, "metadata": metadata, }, vault_update_params.VaultUpdateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsVault, ) def list( self, *, include_archived: bool | Omit = omit, limit: int | Omit = omit, page: str | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncPageCursor[BetaManagedAgentsVault]: """ List Vaults Args: include_archived: Whether to include archived vaults in the results. limit: Maximum number of vaults to return per page. Defaults to 20, maximum 100. page: Opaque pagination token from a previous `list_vaults` response. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get_api_list( "/v1/vaults?beta=true", page=SyncPageCursor[BetaManagedAgentsVault], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "include_archived": include_archived, "limit": limit, "page": page, }, vault_list_params.VaultListParams, ), ), model=BetaManagedAgentsVault, ) def delete( self, vault_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsDeletedVault: """ Delete Vault Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not vault_id: raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._delete( path_template("/v1/vaults/{vault_id}?beta=true", vault_id=vault_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsDeletedVault, ) def archive( self, vault_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsVault: """ Archive Vault Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not vault_id: raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._post( path_template("/v1/vaults/{vault_id}/archive?beta=true", vault_id=vault_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsVault, ) class AsyncVaults(AsyncAPIResource): @cached_property def credentials(self) -> AsyncCredentials: return AsyncCredentials(self._client) @cached_property def with_raw_response(self) -> AsyncVaultsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncVaultsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncVaultsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncVaultsWithStreamingResponse(self) async def create( self, *, display_name: str, metadata: Dict[str, str] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsVault: """Create Vault Args: display_name: Human-readable name for the vault. 1-255 characters. metadata: Arbitrary key-value metadata to attach to the vault. Maximum 16 pairs, keys up to 64 chars, values up to 512 chars. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._post( "/v1/vaults?beta=true", body=await async_maybe_transform( { "display_name": display_name, "metadata": metadata, }, vault_create_params.VaultCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsVault, ) async def retrieve( self, vault_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsVault: """ Get Vault Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not vault_id: raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._get( path_template("/v1/vaults/{vault_id}?beta=true", vault_id=vault_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsVault, ) async def update( self, vault_id: str, *, display_name: Optional[str] | Omit = omit, metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsVault: """Update Vault Args: display_name: Updated human-readable name for the vault. 1-255 characters. metadata: Metadata patch. Set a key to a string to upsert it, or to null to delete it. Omitted keys are preserved. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not vault_id: raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._post( path_template("/v1/vaults/{vault_id}?beta=true", vault_id=vault_id), body=await async_maybe_transform( { "display_name": display_name, "metadata": metadata, }, vault_update_params.VaultUpdateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsVault, ) def list( self, *, include_archived: bool | Omit = omit, limit: int | Omit = omit, page: str | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[BetaManagedAgentsVault, AsyncPageCursor[BetaManagedAgentsVault]]: """ List Vaults Args: include_archived: Whether to include archived vaults in the results. limit: Maximum number of vaults to return per page. Defaults to 20, maximum 100. page: Opaque pagination token from a previous `list_vaults` response. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return self._get_api_list( "/v1/vaults?beta=true", page=AsyncPageCursor[BetaManagedAgentsVault], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "include_archived": include_archived, "limit": limit, "page": page, }, vault_list_params.VaultListParams, ), ), model=BetaManagedAgentsVault, ) async def delete( self, vault_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsDeletedVault: """ Delete Vault Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not vault_id: raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._delete( path_template("/v1/vaults/{vault_id}?beta=true", vault_id=vault_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsDeletedVault, ) async def archive( self, vault_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> BetaManagedAgentsVault: """ Archive Vault Args: betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not vault_id: raise ValueError(f"Expected a non-empty value for `vault_id` but received {vault_id!r}") extra_headers = { **strip_not_given( { "anthropic-beta": ",".join(chain((str(e) for e in betas), ["managed-agents-2026-04-01"])) if is_given(betas) else not_given } ), **(extra_headers or {}), } extra_headers = {"anthropic-beta": "managed-agents-2026-04-01", **(extra_headers or {})} return await self._post( path_template("/v1/vaults/{vault_id}/archive?beta=true", vault_id=vault_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=BetaManagedAgentsVault, ) class VaultsWithRawResponse: def __init__(self, vaults: Vaults) -> None: self._vaults = vaults self.create = _legacy_response.to_raw_response_wrapper( vaults.create, ) self.retrieve = _legacy_response.to_raw_response_wrapper( vaults.retrieve, ) self.update = _legacy_response.to_raw_response_wrapper( vaults.update, ) self.list = _legacy_response.to_raw_response_wrapper( vaults.list, ) self.delete = _legacy_response.to_raw_response_wrapper( vaults.delete, ) self.archive = _legacy_response.to_raw_response_wrapper( vaults.archive, ) @cached_property def credentials(self) -> CredentialsWithRawResponse: return CredentialsWithRawResponse(self._vaults.credentials) class AsyncVaultsWithRawResponse: def __init__(self, vaults: AsyncVaults) -> None: self._vaults = vaults self.create = _legacy_response.async_to_raw_response_wrapper( vaults.create, ) self.retrieve = _legacy_response.async_to_raw_response_wrapper( vaults.retrieve, ) self.update = _legacy_response.async_to_raw_response_wrapper( vaults.update, ) self.list = _legacy_response.async_to_raw_response_wrapper( vaults.list, ) self.delete = _legacy_response.async_to_raw_response_wrapper( vaults.delete, ) self.archive = _legacy_response.async_to_raw_response_wrapper( vaults.archive, ) @cached_property def credentials(self) -> AsyncCredentialsWithRawResponse: return AsyncCredentialsWithRawResponse(self._vaults.credentials) class VaultsWithStreamingResponse: def __init__(self, vaults: Vaults) -> None: self._vaults = vaults self.create = to_streamed_response_wrapper( vaults.create, ) self.retrieve = to_streamed_response_wrapper( vaults.retrieve, ) self.update = to_streamed_response_wrapper( vaults.update, ) self.list = to_streamed_response_wrapper( vaults.list, ) self.delete = to_streamed_response_wrapper( vaults.delete, ) self.archive = to_streamed_response_wrapper( vaults.archive, ) @cached_property def credentials(self) -> CredentialsWithStreamingResponse: return CredentialsWithStreamingResponse(self._vaults.credentials) class AsyncVaultsWithStreamingResponse: def __init__(self, vaults: AsyncVaults) -> None: self._vaults = vaults self.create = async_to_streamed_response_wrapper( vaults.create, ) self.retrieve = async_to_streamed_response_wrapper( vaults.retrieve, ) self.update = async_to_streamed_response_wrapper( vaults.update, ) self.list = async_to_streamed_response_wrapper( vaults.list, ) self.delete = async_to_streamed_response_wrapper( vaults.delete, ) self.archive = async_to_streamed_response_wrapper( vaults.archive, ) @cached_property def credentials(self) -> AsyncCredentialsWithStreamingResponse: return AsyncCredentialsWithStreamingResponse(self._vaults.credentials) anthropic-sdk-python-0.120.2/src/anthropic/resources/beta/webhooks.py000066400000000000000000000044151523216435200256460ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import json from typing import Mapping, cast from ..._models import construct_type from ..._resource import SyncAPIResource, AsyncAPIResource from ..._exceptions import AnthropicError from ...types.beta.unwrap_webhook_event import UnwrapWebhookEvent __all__ = ["Webhooks", "AsyncWebhooks"] class Webhooks(SyncAPIResource): def unwrap(self, payload: str, *, headers: Mapping[str, str], key: str | bytes | None = None) -> UnwrapWebhookEvent: try: from standardwebhooks import Webhook except ImportError as exc: raise AnthropicError("You need to install `anthropic[webhooks]` to use this method") from exc if key is None: key = self._client.webhook_key if key is None: raise ValueError( "Cannot verify a webhook without a key on either the client's webhook_key or passed in as an argument" ) if not isinstance(headers, dict): headers = dict(headers) Webhook(key).verify(payload, headers) return cast( UnwrapWebhookEvent, construct_type( type_=UnwrapWebhookEvent, value=json.loads(payload), ), ) class AsyncWebhooks(AsyncAPIResource): def unwrap(self, payload: str, *, headers: Mapping[str, str], key: str | bytes | None = None) -> UnwrapWebhookEvent: try: from standardwebhooks import Webhook except ImportError as exc: raise AnthropicError("You need to install `anthropic[webhooks]` to use this method") from exc if key is None: key = self._client.webhook_key if key is None: raise ValueError( "Cannot verify a webhook without a key on either the client's webhook_key or passed in as an argument" ) if not isinstance(headers, dict): headers = dict(headers) Webhook(key).verify(payload, headers) return cast( UnwrapWebhookEvent, construct_type( type_=UnwrapWebhookEvent, value=json.loads(payload), ), ) anthropic-sdk-python-0.120.2/src/anthropic/resources/completions.py000066400000000000000000001065741523216435200254570ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List from typing_extensions import Literal, overload import httpx from .. import _legacy_response from ..types import completion_create_params from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given from .._utils import is_given, required_args, maybe_transform, strip_not_given, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from .._constants import DEFAULT_TIMEOUT from .._streaming import Stream, AsyncStream from .._base_client import make_request_options from ..types.completion import Completion from ..types.model_param import ModelParam from ..types.metadata_param import MetadataParam from ..types.anthropic_beta_param import AnthropicBetaParam __all__ = ["Completions", "AsyncCompletions"] class Completions(SyncAPIResource): @cached_property def with_raw_response(self) -> CompletionsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return CompletionsWithRawResponse(self) @cached_property def with_streaming_response(self) -> CompletionsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return CompletionsWithStreamingResponse(self) @overload def create( self, *, max_tokens_to_sample: int, model: ModelParam, prompt: str, metadata: MetadataParam | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, stream: Literal[False] | Omit = omit, temperature: float | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Completion: """[Legacy] Create a Text Completion. The Text Completions API is a legacy API. We recommend using the [Messages API](https://platform.claude.com/docs/en/api/messages) going forward. Future models and features will not be compatible with Text Completions. See our [migration guide](https://platform.claude.com/docs/en/build-with-claude/working-with-messages) for guidance in migrating from Text Completions to Messages. Args: max_tokens_to_sample: The maximum number of tokens to generate before stopping. Note that our models may stop _before_ reaching this maximum. This parameter only specifies the absolute maximum number of tokens to generate. model: The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. prompt: The prompt that you want Claude to complete. For proper response generation you will need to format your prompt using alternating `\n\nHuman:` and `\n\nAssistant:` conversational turns. For example: ``` "\n\nHuman: {userQuestion}\n\nAssistant:" ``` See [prompt validation](https://platform.claude.com/docs/en/build-with-claude/working-with-messages) and our guide to [prompt design](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/overview) for more details. metadata: An object describing metadata about the request. stop_sequences: Sequences that will cause the model to stop generating. Our models stop on `"\n\nHuman:"`, and may include additional built-in stop sequences in the future. By providing the stop_sequences parameter, you may include additional strings that will cause the model to stop generating. stream: Whether to incrementally stream the response using server-sent events. See [streaming](https://platform.claude.com/docs/en/build-with-claude/streaming) for details. temperature: Amount of randomness injected into the response. Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` for analytical / multiple choice, and closer to `1.0` for creative and generative tasks. Note that even with `temperature` of `0.0`, the results will not be fully deterministic. top_k: Only sample from the top K options for each subsequent token. Used to remove "long tail" low probability responses. [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). Recommended for advanced use cases only. top_p: Use nucleus sampling. In nucleus sampling, we compute the cumulative distribution over all the options for each subsequent token in decreasing probability order and cut it off once it reaches a particular probability specified by `top_p`. Recommended for advanced use cases only. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ ... @overload def create( self, *, max_tokens_to_sample: int, model: ModelParam, prompt: str, stream: Literal[True], metadata: MetadataParam | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, temperature: float | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Stream[Completion]: """[Legacy] Create a Text Completion. The Text Completions API is a legacy API. We recommend using the [Messages API](https://platform.claude.com/docs/en/api/messages) going forward. Future models and features will not be compatible with Text Completions. See our [migration guide](https://platform.claude.com/docs/en/build-with-claude/working-with-messages) for guidance in migrating from Text Completions to Messages. Args: max_tokens_to_sample: The maximum number of tokens to generate before stopping. Note that our models may stop _before_ reaching this maximum. This parameter only specifies the absolute maximum number of tokens to generate. model: The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. prompt: The prompt that you want Claude to complete. For proper response generation you will need to format your prompt using alternating `\n\nHuman:` and `\n\nAssistant:` conversational turns. For example: ``` "\n\nHuman: {userQuestion}\n\nAssistant:" ``` See [prompt validation](https://platform.claude.com/docs/en/build-with-claude/working-with-messages) and our guide to [prompt design](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/overview) for more details. stream: Whether to incrementally stream the response using server-sent events. See [streaming](https://platform.claude.com/docs/en/build-with-claude/streaming) for details. metadata: An object describing metadata about the request. stop_sequences: Sequences that will cause the model to stop generating. Our models stop on `"\n\nHuman:"`, and may include additional built-in stop sequences in the future. By providing the stop_sequences parameter, you may include additional strings that will cause the model to stop generating. temperature: Amount of randomness injected into the response. Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` for analytical / multiple choice, and closer to `1.0` for creative and generative tasks. Note that even with `temperature` of `0.0`, the results will not be fully deterministic. top_k: Only sample from the top K options for each subsequent token. Used to remove "long tail" low probability responses. [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). Recommended for advanced use cases only. top_p: Use nucleus sampling. In nucleus sampling, we compute the cumulative distribution over all the options for each subsequent token in decreasing probability order and cut it off once it reaches a particular probability specified by `top_p`. Recommended for advanced use cases only. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ ... @overload def create( self, *, max_tokens_to_sample: int, model: ModelParam, prompt: str, stream: bool, metadata: MetadataParam | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, temperature: float | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Completion | Stream[Completion]: """[Legacy] Create a Text Completion. The Text Completions API is a legacy API. We recommend using the [Messages API](https://platform.claude.com/docs/en/api/messages) going forward. Future models and features will not be compatible with Text Completions. See our [migration guide](https://platform.claude.com/docs/en/build-with-claude/working-with-messages) for guidance in migrating from Text Completions to Messages. Args: max_tokens_to_sample: The maximum number of tokens to generate before stopping. Note that our models may stop _before_ reaching this maximum. This parameter only specifies the absolute maximum number of tokens to generate. model: The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. prompt: The prompt that you want Claude to complete. For proper response generation you will need to format your prompt using alternating `\n\nHuman:` and `\n\nAssistant:` conversational turns. For example: ``` "\n\nHuman: {userQuestion}\n\nAssistant:" ``` See [prompt validation](https://platform.claude.com/docs/en/build-with-claude/working-with-messages) and our guide to [prompt design](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/overview) for more details. stream: Whether to incrementally stream the response using server-sent events. See [streaming](https://platform.claude.com/docs/en/build-with-claude/streaming) for details. metadata: An object describing metadata about the request. stop_sequences: Sequences that will cause the model to stop generating. Our models stop on `"\n\nHuman:"`, and may include additional built-in stop sequences in the future. By providing the stop_sequences parameter, you may include additional strings that will cause the model to stop generating. temperature: Amount of randomness injected into the response. Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` for analytical / multiple choice, and closer to `1.0` for creative and generative tasks. Note that even with `temperature` of `0.0`, the results will not be fully deterministic. top_k: Only sample from the top K options for each subsequent token. Used to remove "long tail" low probability responses. [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). Recommended for advanced use cases only. top_p: Use nucleus sampling. In nucleus sampling, we compute the cumulative distribution over all the options for each subsequent token in decreasing probability order and cut it off once it reaches a particular probability specified by `top_p`. Recommended for advanced use cases only. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ ... @required_args(["max_tokens_to_sample", "model", "prompt"], ["max_tokens_to_sample", "model", "prompt", "stream"]) def create( self, *, max_tokens_to_sample: int, model: ModelParam, prompt: str, metadata: MetadataParam | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, stream: Literal[False] | Literal[True] | Omit = omit, temperature: float | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Completion | Stream[Completion]: if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: timeout = 600 extra_headers = { **strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), **(extra_headers or {}), } return self._post( "/v1/complete", body=maybe_transform( { "max_tokens_to_sample": max_tokens_to_sample, "model": model, "prompt": prompt, "metadata": metadata, "stop_sequences": stop_sequences, "stream": stream, "temperature": temperature, "top_k": top_k, "top_p": top_p, }, completion_create_params.CompletionCreateParamsStreaming if stream else completion_create_params.CompletionCreateParamsNonStreaming, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=Completion, stream=stream or False, stream_cls=Stream[Completion], ) class AsyncCompletions(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncCompletionsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncCompletionsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncCompletionsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncCompletionsWithStreamingResponse(self) @overload async def create( self, *, max_tokens_to_sample: int, model: ModelParam, prompt: str, metadata: MetadataParam | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, stream: Literal[False] | Omit = omit, temperature: float | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Completion: """[Legacy] Create a Text Completion. The Text Completions API is a legacy API. We recommend using the [Messages API](https://platform.claude.com/docs/en/api/messages) going forward. Future models and features will not be compatible with Text Completions. See our [migration guide](https://platform.claude.com/docs/en/build-with-claude/working-with-messages) for guidance in migrating from Text Completions to Messages. Args: max_tokens_to_sample: The maximum number of tokens to generate before stopping. Note that our models may stop _before_ reaching this maximum. This parameter only specifies the absolute maximum number of tokens to generate. model: The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. prompt: The prompt that you want Claude to complete. For proper response generation you will need to format your prompt using alternating `\n\nHuman:` and `\n\nAssistant:` conversational turns. For example: ``` "\n\nHuman: {userQuestion}\n\nAssistant:" ``` See [prompt validation](https://platform.claude.com/docs/en/build-with-claude/working-with-messages) and our guide to [prompt design](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/overview) for more details. metadata: An object describing metadata about the request. stop_sequences: Sequences that will cause the model to stop generating. Our models stop on `"\n\nHuman:"`, and may include additional built-in stop sequences in the future. By providing the stop_sequences parameter, you may include additional strings that will cause the model to stop generating. stream: Whether to incrementally stream the response using server-sent events. See [streaming](https://platform.claude.com/docs/en/build-with-claude/streaming) for details. temperature: Amount of randomness injected into the response. Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` for analytical / multiple choice, and closer to `1.0` for creative and generative tasks. Note that even with `temperature` of `0.0`, the results will not be fully deterministic. top_k: Only sample from the top K options for each subsequent token. Used to remove "long tail" low probability responses. [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). Recommended for advanced use cases only. top_p: Use nucleus sampling. In nucleus sampling, we compute the cumulative distribution over all the options for each subsequent token in decreasing probability order and cut it off once it reaches a particular probability specified by `top_p`. Recommended for advanced use cases only. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ ... @overload async def create( self, *, max_tokens_to_sample: int, model: ModelParam, prompt: str, stream: Literal[True], metadata: MetadataParam | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, temperature: float | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncStream[Completion]: """[Legacy] Create a Text Completion. The Text Completions API is a legacy API. We recommend using the [Messages API](https://platform.claude.com/docs/en/api/messages) going forward. Future models and features will not be compatible with Text Completions. See our [migration guide](https://platform.claude.com/docs/en/build-with-claude/working-with-messages) for guidance in migrating from Text Completions to Messages. Args: max_tokens_to_sample: The maximum number of tokens to generate before stopping. Note that our models may stop _before_ reaching this maximum. This parameter only specifies the absolute maximum number of tokens to generate. model: The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. prompt: The prompt that you want Claude to complete. For proper response generation you will need to format your prompt using alternating `\n\nHuman:` and `\n\nAssistant:` conversational turns. For example: ``` "\n\nHuman: {userQuestion}\n\nAssistant:" ``` See [prompt validation](https://platform.claude.com/docs/en/build-with-claude/working-with-messages) and our guide to [prompt design](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/overview) for more details. stream: Whether to incrementally stream the response using server-sent events. See [streaming](https://platform.claude.com/docs/en/build-with-claude/streaming) for details. metadata: An object describing metadata about the request. stop_sequences: Sequences that will cause the model to stop generating. Our models stop on `"\n\nHuman:"`, and may include additional built-in stop sequences in the future. By providing the stop_sequences parameter, you may include additional strings that will cause the model to stop generating. temperature: Amount of randomness injected into the response. Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` for analytical / multiple choice, and closer to `1.0` for creative and generative tasks. Note that even with `temperature` of `0.0`, the results will not be fully deterministic. top_k: Only sample from the top K options for each subsequent token. Used to remove "long tail" low probability responses. [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). Recommended for advanced use cases only. top_p: Use nucleus sampling. In nucleus sampling, we compute the cumulative distribution over all the options for each subsequent token in decreasing probability order and cut it off once it reaches a particular probability specified by `top_p`. Recommended for advanced use cases only. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ ... @overload async def create( self, *, max_tokens_to_sample: int, model: ModelParam, prompt: str, stream: bool, metadata: MetadataParam | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, temperature: float | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Completion | AsyncStream[Completion]: """[Legacy] Create a Text Completion. The Text Completions API is a legacy API. We recommend using the [Messages API](https://platform.claude.com/docs/en/api/messages) going forward. Future models and features will not be compatible with Text Completions. See our [migration guide](https://platform.claude.com/docs/en/build-with-claude/working-with-messages) for guidance in migrating from Text Completions to Messages. Args: max_tokens_to_sample: The maximum number of tokens to generate before stopping. Note that our models may stop _before_ reaching this maximum. This parameter only specifies the absolute maximum number of tokens to generate. model: The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. prompt: The prompt that you want Claude to complete. For proper response generation you will need to format your prompt using alternating `\n\nHuman:` and `\n\nAssistant:` conversational turns. For example: ``` "\n\nHuman: {userQuestion}\n\nAssistant:" ``` See [prompt validation](https://platform.claude.com/docs/en/build-with-claude/working-with-messages) and our guide to [prompt design](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/overview) for more details. stream: Whether to incrementally stream the response using server-sent events. See [streaming](https://platform.claude.com/docs/en/build-with-claude/streaming) for details. metadata: An object describing metadata about the request. stop_sequences: Sequences that will cause the model to stop generating. Our models stop on `"\n\nHuman:"`, and may include additional built-in stop sequences in the future. By providing the stop_sequences parameter, you may include additional strings that will cause the model to stop generating. temperature: Amount of randomness injected into the response. Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` for analytical / multiple choice, and closer to `1.0` for creative and generative tasks. Note that even with `temperature` of `0.0`, the results will not be fully deterministic. top_k: Only sample from the top K options for each subsequent token. Used to remove "long tail" low probability responses. [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). Recommended for advanced use cases only. top_p: Use nucleus sampling. In nucleus sampling, we compute the cumulative distribution over all the options for each subsequent token in decreasing probability order and cut it off once it reaches a particular probability specified by `top_p`. Recommended for advanced use cases only. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ ... @required_args(["max_tokens_to_sample", "model", "prompt"], ["max_tokens_to_sample", "model", "prompt", "stream"]) async def create( self, *, max_tokens_to_sample: int, model: ModelParam, prompt: str, metadata: MetadataParam | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, stream: Literal[False] | Literal[True] | Omit = omit, temperature: float | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Completion | AsyncStream[Completion]: if not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: timeout = 600 extra_headers = { **strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), **(extra_headers or {}), } return await self._post( "/v1/complete", body=await async_maybe_transform( { "max_tokens_to_sample": max_tokens_to_sample, "model": model, "prompt": prompt, "metadata": metadata, "stop_sequences": stop_sequences, "stream": stream, "temperature": temperature, "top_k": top_k, "top_p": top_p, }, completion_create_params.CompletionCreateParamsStreaming if stream else completion_create_params.CompletionCreateParamsNonStreaming, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=Completion, stream=stream or False, stream_cls=AsyncStream[Completion], ) class CompletionsWithRawResponse: def __init__(self, completions: Completions) -> None: self._completions = completions self.create = _legacy_response.to_raw_response_wrapper( completions.create, ) class AsyncCompletionsWithRawResponse: def __init__(self, completions: AsyncCompletions) -> None: self._completions = completions self.create = _legacy_response.async_to_raw_response_wrapper( completions.create, ) class CompletionsWithStreamingResponse: def __init__(self, completions: Completions) -> None: self._completions = completions self.create = to_streamed_response_wrapper( completions.create, ) class AsyncCompletionsWithStreamingResponse: def __init__(self, completions: AsyncCompletions) -> None: self._completions = completions self.create = async_to_streamed_response_wrapper( completions.create, ) anthropic-sdk-python-0.120.2/src/anthropic/resources/messages/000077500000000000000000000000001523216435200243435ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/resources/messages/__init__.py000066400000000000000000000016011523216435200264520ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .batches import ( Batches, AsyncBatches, BatchesWithRawResponse, AsyncBatchesWithRawResponse, BatchesWithStreamingResponse, AsyncBatchesWithStreamingResponse, ) from .messages import ( DEPRECATED_MODELS, Messages, AsyncMessages, MessagesWithRawResponse, AsyncMessagesWithRawResponse, MessagesWithStreamingResponse, AsyncMessagesWithStreamingResponse, ) __all__ = [ "Batches", "AsyncBatches", "BatchesWithRawResponse", "AsyncBatchesWithRawResponse", "BatchesWithStreamingResponse", "AsyncBatchesWithStreamingResponse", "Messages", "AsyncMessages", "MessagesWithRawResponse", "AsyncMessagesWithRawResponse", "MessagesWithStreamingResponse", "AsyncMessagesWithStreamingResponse", "DEPRECATED_MODELS", ] anthropic-sdk-python-0.120.2/src/anthropic/resources/messages/batches.py000066400000000000000000000732501523216435200263350ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Iterable import httpx from ... import _legacy_response from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from ..._utils import path_template, maybe_transform, strip_not_given, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ...pagination import SyncPage, AsyncPage from ..._exceptions import AnthropicError from ..._base_client import AsyncPaginator, make_request_options from ...types.messages import batch_list_params, batch_create_params from ..._decoders.jsonl import JSONLDecoder, AsyncJSONLDecoder from ...types.messages.message_batch import MessageBatch from ...types.messages.deleted_message_batch import DeletedMessageBatch from ...types.messages.message_batch_individual_response import MessageBatchIndividualResponse __all__ = ["Batches", "AsyncBatches"] class Batches(SyncAPIResource): @cached_property def with_raw_response(self) -> BatchesWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return BatchesWithRawResponse(self) @cached_property def with_streaming_response(self) -> BatchesWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return BatchesWithStreamingResponse(self) def create( self, *, requests: Iterable[batch_create_params.Request], user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> MessageBatch: """ Send a batch of Message creation requests. The Message Batches API can be used to process multiple Messages API requests at once. Once a Message Batch is created, it begins processing immediately. Batches can take up to 24 hours to complete. Learn more about the Message Batches API in our [user guide](https://platform.claude.com/docs/en/build-with-claude/batch-processing) Args: requests: List of requests for prompt completion. Each is an individual request to create a Message. user_profile_id: The user profile ID to attribute the requests in this batch to. Use when acting on behalf of a party other than your organization. Requires the `user-profiles` beta header. Applies to every request in the batch; an individual request whose `user_profile_id` body field conflicts with this header is errored. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = {**strip_not_given({"anthropic-user-profile-id": user_profile_id}), **(extra_headers or {})} return self._post( "/v1/messages/batches", body=maybe_transform({"requests": requests}, batch_create_params.BatchCreateParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=MessageBatch, ) def retrieve( self, message_batch_id: str, *, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> MessageBatch: """This endpoint is idempotent and can be used to poll for Message Batch completion. To access the results of a Message Batch, make a request to the `results_url` field in the response. Learn more about the Message Batches API in our [user guide](https://platform.claude.com/docs/en/build-with-claude/batch-processing) Args: message_batch_id: ID of the Message Batch. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not message_batch_id: raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") return self._get( path_template("/v1/messages/batches/{message_batch_id}", message_batch_id=message_batch_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=MessageBatch, ) def list( self, *, after_id: str | Omit = omit, before_id: str | Omit = omit, limit: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncPage[MessageBatch]: """List all Message Batches within a Workspace. Most recently created batches are returned first. Learn more about the Message Batches API in our [user guide](https://platform.claude.com/docs/en/build-with-claude/batch-processing) Args: after_id: ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately after this object. before_id: ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately before this object. limit: Number of items to return per page. Defaults to `20`. Ranges from `1` to `1000`. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( "/v1/messages/batches", page=SyncPage[MessageBatch], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "after_id": after_id, "before_id": before_id, "limit": limit, }, batch_list_params.BatchListParams, ), ), model=MessageBatch, ) def delete( self, message_batch_id: str, *, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> DeletedMessageBatch: """ Delete a Message Batch. Message Batches can only be deleted once they've finished processing. If you'd like to delete an in-progress batch, you must first cancel it. Learn more about the Message Batches API in our [user guide](https://platform.claude.com/docs/en/build-with-claude/batch-processing) Args: message_batch_id: ID of the Message Batch. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not message_batch_id: raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") return self._delete( path_template("/v1/messages/batches/{message_batch_id}", message_batch_id=message_batch_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=DeletedMessageBatch, ) def cancel( self, message_batch_id: str, *, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> MessageBatch: """Batches may be canceled any time before processing ends. Once cancellation is initiated, the batch enters a `canceling` state, at which time the system may complete any in-progress, non-interruptible requests before finalizing cancellation. The number of canceled requests is specified in `request_counts`. To determine which requests were canceled, check the individual results within the batch. Note that cancellation may not result in any canceled requests if they were non-interruptible. Learn more about the Message Batches API in our [user guide](https://platform.claude.com/docs/en/build-with-claude/batch-processing) Args: message_batch_id: ID of the Message Batch. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not message_batch_id: raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") return self._post( path_template("/v1/messages/batches/{message_batch_id}/cancel", message_batch_id=message_batch_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=MessageBatch, ) def results( self, message_batch_id: str, *, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> JSONLDecoder[MessageBatchIndividualResponse]: """ Streams the results of a Message Batch as a `.jsonl` file. Each line in the file is a JSON object containing the result of a single request in the Message Batch. Results are not guaranteed to be in the same order as requests. Use the `custom_id` field to match results to requests. Learn more about the Message Batches API in our [user guide](https://platform.claude.com/docs/en/build-with-claude/batch-processing) Args: message_batch_id: ID of the Message Batch. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not message_batch_id: raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") batch = self.retrieve(message_batch_id=message_batch_id) if not batch.results_url: raise AnthropicError( f"No `results_url` for the given batch; Has it finished processing? {batch.processing_status}" ) extra_headers = {"Accept": "application/binary", **(extra_headers or {})} return self._get( path_template(batch.results_url, message_batch_id=message_batch_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=JSONLDecoder[MessageBatchIndividualResponse], stream=True, ) class AsyncBatches(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncBatchesWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncBatchesWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncBatchesWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncBatchesWithStreamingResponse(self) async def create( self, *, requests: Iterable[batch_create_params.Request], user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> MessageBatch: """ Send a batch of Message creation requests. The Message Batches API can be used to process multiple Messages API requests at once. Once a Message Batch is created, it begins processing immediately. Batches can take up to 24 hours to complete. Learn more about the Message Batches API in our [user guide](https://platform.claude.com/docs/en/build-with-claude/batch-processing) Args: requests: List of requests for prompt completion. Each is an individual request to create a Message. user_profile_id: The user profile ID to attribute the requests in this batch to. Use when acting on behalf of a party other than your organization. Requires the `user-profiles` beta header. Applies to every request in the batch; an individual request whose `user_profile_id` body field conflicts with this header is errored. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = {**strip_not_given({"anthropic-user-profile-id": user_profile_id}), **(extra_headers or {})} return await self._post( "/v1/messages/batches", body=await async_maybe_transform({"requests": requests}, batch_create_params.BatchCreateParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=MessageBatch, ) async def retrieve( self, message_batch_id: str, *, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> MessageBatch: """This endpoint is idempotent and can be used to poll for Message Batch completion. To access the results of a Message Batch, make a request to the `results_url` field in the response. Learn more about the Message Batches API in our [user guide](https://platform.claude.com/docs/en/build-with-claude/batch-processing) Args: message_batch_id: ID of the Message Batch. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not message_batch_id: raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") return await self._get( path_template("/v1/messages/batches/{message_batch_id}", message_batch_id=message_batch_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=MessageBatch, ) def list( self, *, after_id: str | Omit = omit, before_id: str | Omit = omit, limit: int | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[MessageBatch, AsyncPage[MessageBatch]]: """List all Message Batches within a Workspace. Most recently created batches are returned first. Learn more about the Message Batches API in our [user guide](https://platform.claude.com/docs/en/build-with-claude/batch-processing) Args: after_id: ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately after this object. before_id: ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately before this object. limit: Number of items to return per page. Defaults to `20`. Ranges from `1` to `1000`. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ return self._get_api_list( "/v1/messages/batches", page=AsyncPage[MessageBatch], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "after_id": after_id, "before_id": before_id, "limit": limit, }, batch_list_params.BatchListParams, ), ), model=MessageBatch, ) async def delete( self, message_batch_id: str, *, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> DeletedMessageBatch: """ Delete a Message Batch. Message Batches can only be deleted once they've finished processing. If you'd like to delete an in-progress batch, you must first cancel it. Learn more about the Message Batches API in our [user guide](https://platform.claude.com/docs/en/build-with-claude/batch-processing) Args: message_batch_id: ID of the Message Batch. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not message_batch_id: raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") return await self._delete( path_template("/v1/messages/batches/{message_batch_id}", message_batch_id=message_batch_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=DeletedMessageBatch, ) async def cancel( self, message_batch_id: str, *, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> MessageBatch: """Batches may be canceled any time before processing ends. Once cancellation is initiated, the batch enters a `canceling` state, at which time the system may complete any in-progress, non-interruptible requests before finalizing cancellation. The number of canceled requests is specified in `request_counts`. To determine which requests were canceled, check the individual results within the batch. Note that cancellation may not result in any canceled requests if they were non-interruptible. Learn more about the Message Batches API in our [user guide](https://platform.claude.com/docs/en/build-with-claude/batch-processing) Args: message_batch_id: ID of the Message Batch. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not message_batch_id: raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") return await self._post( path_template("/v1/messages/batches/{message_batch_id}/cancel", message_batch_id=message_batch_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=MessageBatch, ) async def results( self, message_batch_id: str, *, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncJSONLDecoder[MessageBatchIndividualResponse]: """ Streams the results of a Message Batch as a `.jsonl` file. Each line in the file is a JSON object containing the result of a single request in the Message Batch. Results are not guaranteed to be in the same order as requests. Use the `custom_id` field to match results to requests. Learn more about the Message Batches API in our [user guide](https://platform.claude.com/docs/en/build-with-claude/batch-processing) Args: message_batch_id: ID of the Message Batch. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not message_batch_id: raise ValueError(f"Expected a non-empty value for `message_batch_id` but received {message_batch_id!r}") batch = await self.retrieve(message_batch_id=message_batch_id) if not batch.results_url: raise AnthropicError( f"No `results_url` for the given batch; Has it finished processing? {batch.processing_status}" ) extra_headers = {"Accept": "application/binary", **(extra_headers or {})} return await self._get( path_template(batch.results_url, message_batch_id=message_batch_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=AsyncJSONLDecoder[MessageBatchIndividualResponse], stream=True, ) class BatchesWithRawResponse: def __init__(self, batches: Batches) -> None: self._batches = batches self.create = _legacy_response.to_raw_response_wrapper( batches.create, ) self.retrieve = _legacy_response.to_raw_response_wrapper( batches.retrieve, ) self.list = _legacy_response.to_raw_response_wrapper( batches.list, ) self.delete = _legacy_response.to_raw_response_wrapper( batches.delete, ) self.cancel = _legacy_response.to_raw_response_wrapper( batches.cancel, ) class AsyncBatchesWithRawResponse: def __init__(self, batches: AsyncBatches) -> None: self._batches = batches self.create = _legacy_response.async_to_raw_response_wrapper( batches.create, ) self.retrieve = _legacy_response.async_to_raw_response_wrapper( batches.retrieve, ) self.list = _legacy_response.async_to_raw_response_wrapper( batches.list, ) self.delete = _legacy_response.async_to_raw_response_wrapper( batches.delete, ) self.cancel = _legacy_response.async_to_raw_response_wrapper( batches.cancel, ) class BatchesWithStreamingResponse: def __init__(self, batches: Batches) -> None: self._batches = batches self.create = to_streamed_response_wrapper( batches.create, ) self.retrieve = to_streamed_response_wrapper( batches.retrieve, ) self.list = to_streamed_response_wrapper( batches.list, ) self.delete = to_streamed_response_wrapper( batches.delete, ) self.cancel = to_streamed_response_wrapper( batches.cancel, ) class AsyncBatchesWithStreamingResponse: def __init__(self, batches: AsyncBatches) -> None: self._batches = batches self.create = async_to_streamed_response_wrapper( batches.create, ) self.retrieve = async_to_streamed_response_wrapper( batches.retrieve, ) self.list = async_to_streamed_response_wrapper( batches.list, ) self.delete = async_to_streamed_response_wrapper( batches.delete, ) self.cancel = async_to_streamed_response_wrapper( batches.cancel, ) anthropic-sdk-python-0.120.2/src/anthropic/resources/messages/messages.py000066400000000000000000004237001523216435200265320ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import warnings from typing import Type, Union, Iterable, Optional, cast from functools import partial from typing_extensions import Literal, overload import httpx import pydantic from ... import _legacy_response from ...types import ( ThinkingConfigParam, message_create_params, message_count_tokens_params, ) from .batches import ( Batches, AsyncBatches, BatchesWithRawResponse, AsyncBatchesWithRawResponse, BatchesWithStreamingResponse, AsyncBatchesWithStreamingResponse, ) from ..._types import NOT_GIVEN, Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given from ..._utils import is_given, required_args, maybe_transform, strip_not_given, async_maybe_transform from ..._compat import cached_property from ..._models import TypeAdapter from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ..._constants import DEFAULT_TIMEOUT, MODEL_NONSTREAMING_TOKENS from ..._streaming import Stream, AsyncStream from ..._base_client import ( merge_headers, make_request_options, ) from ..._utils._utils import is_dict from ...lib.streaming import MessageStreamManager, AsyncMessageStreamManager from ...types.message import Message from ...types.model_param import ModelParam from ...types.message_param import MessageParam from ...lib._parse._response import ResponseFormatT, parse_response from ...types.metadata_param import MetadataParam from ...types.parsed_message import ParsedMessage from ...lib._parse._transform import transform_schema from ...lib._stainless_helpers import ( HELPER_METHOD_STREAM as _HELPER_METHOD_STREAM, STAINLESS_HELPER_METHOD_HEADER as _STAINLESS_HELPER_METHOD_HEADER, STAINLESS_STREAM_HELPER_HEADER as _STAINLESS_STREAM_HELPER_HEADER, helper_header as _helper_header, ) from ...types.text_block_param import TextBlockParam from ...types.tool_union_param import ToolUnionParam from ...types.tool_choice_param import ToolChoiceParam from ...types.output_config_param import OutputConfigParam from ...types.message_tokens_count import MessageTokensCount from ...types.thinking_config_param import ThinkingConfigParam from ...types.json_output_format_param import JSONOutputFormatParam from ...types.raw_message_stream_event import RawMessageStreamEvent from ...types.cache_control_ephemeral_param import CacheControlEphemeralParam from ...types.message_count_tokens_tool_param import MessageCountTokensToolParam __all__ = ["Messages", "AsyncMessages"] DEPRECATED_MODELS = { "claude-1.3": "November 6th, 2024", "claude-1.3-100k": "November 6th, 2024", "claude-instant-1.1": "November 6th, 2024", "claude-instant-1.1-100k": "November 6th, 2024", "claude-instant-1.2": "November 6th, 2024", "claude-3-sonnet-20240229": "July 21st, 2025", "claude-3-opus-20240229": "January 5th, 2026", "claude-2.1": "July 21st, 2025", "claude-2.0": "July 21st, 2025", "claude-3-7-sonnet-latest": "February 19th, 2026", "claude-3-7-sonnet-20250219": "February 19th, 2026", "claude-3-5-haiku-latest": "February 19th, 2026", "claude-3-5-haiku-20241022": "February 19th, 2026", "claude-opus-4-0": "June 15th, 2026", "claude-opus-4-20250514": "June 15th, 2026", "claude-sonnet-4-0": "June 15th, 2026", "claude-sonnet-4-20250514": "June 15th, 2026", "claude-opus-4-1": "August 5th, 2026", "claude-opus-4-1-20250805": "August 5th, 2026", "claude-mythos-preview": "June 30th, 2026", } MODELS_TO_WARN_WITH_THINKING_ENABLED = ["claude-opus-4-6", "claude-mythos-preview"] class Messages(SyncAPIResource): @cached_property def batches(self) -> Batches: return Batches(self._client) @cached_property def with_raw_response(self) -> MessagesWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return MessagesWithRawResponse(self) @cached_property def with_streaming_response(self) -> MessagesWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return MessagesWithStreamingResponse(self) @overload def create( self, *, max_tokens: int, messages: Iterable[MessageParam], model: ModelParam, cache_control: Optional[CacheControlEphemeralParam] | Omit = omit, container: Optional[str] | Omit = omit, inference_geo: Optional[str] | Omit = omit, metadata: MetadataParam | Omit = omit, output_config: OutputConfigParam | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, stream: Literal[False] | Omit = omit, system: Union[str, Iterable[TextBlockParam]] | Omit = omit, temperature: float | Omit = omit, thinking: ThinkingConfigParam | Omit = omit, tool_choice: ToolChoiceParam | Omit = omit, tools: Iterable[ToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Message: """ Send a structured list of input messages with text and/or image content, and the model will generate the next message in the conversation. The Messages API can be used for either single queries or stateless multi-turn conversations. Learn more about the Messages API in our [user guide](https://platform.claude.com/docs/en/get-started) Args: max_tokens: The maximum number of tokens to generate before stopping. Note that our models may stop _before_ reaching this maximum. This parameter only specifies the absolute maximum number of tokens to generate. Set to `0` to populate the [prompt cache](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pre-warming-the-cache) without generating a response. Different models have different maximum values for this parameter. See [models](https://platform.claude.com/docs/en/about-claude/models/overview) for details. messages: Input messages. Our models are trained to operate on alternating `user` and `assistant` conversational turns. When creating a new `Message`, you specify the prior conversational turns with the `messages` parameter, and the model then generates the next `Message` in the conversation. Consecutive `user` or `assistant` turns in your request will be combined into a single turn. Each input message must be an object with a `role` and `content`. You can specify a single `user`-role message, or you can include multiple `user` and `assistant` messages. If the final message uses the `assistant` role, the response content will continue immediately from the content in that message. This can be used to constrain part of the model's response. Example with a single `user` message: ```json [{ "role": "user", "content": "Hello, Claude" }] ``` Example with multiple conversational turns: ```json [ { "role": "user", "content": "Hello there." }, { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, { "role": "user", "content": "Can you explain LLMs in plain English?" } ] ``` Example with a partially-filled response from Claude: ```json [ { "role": "user", "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" }, { "role": "assistant", "content": "The best answer is (" } ] ``` Each input message `content` may be either a single `string` or an array of content blocks, where each block has a specific `type`. Using a `string` for `content` is shorthand for an array of one content block of type `"text"`. The following input messages are equivalent: ```json { "role": "user", "content": "Hello, Claude" } ``` ```json { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } ``` See [input examples](https://platform.claude.com/docs/en/build-with-claude/working-with-messages). Note that if you want to include a [system prompt](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role), you can use the top-level `system` parameter — there is no `"system"` role for input messages in the Messages API. There is a limit of 100,000 messages in a single request. model: The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. cache_control: Top-level cache control automatically applies a cache_control marker to the last cacheable block in the request. container: Container identifier for reuse across requests. inference_geo: Specifies the geographic region for inference processing. If not specified, the workspace's `default_inference_geo` is used. metadata: An object describing metadata about the request. output_config: Configuration options for the model's output, such as the output format. service_tier: Determines whether to use priority capacity (if available) or standard capacity for this request. Anthropic offers different levels of service for your API requests. See [service-tiers](https://platform.claude.com/docs/en/api/service-tiers) for details. stop_sequences: Custom text sequences that will cause the model to stop generating. Our models will normally stop when they have naturally completed their turn, which will result in a response `stop_reason` of `"end_turn"`. If you want the model to stop generating when it encounters custom strings of text, you can use the `stop_sequences` parameter. If the model encounters one of the custom sequences, the response `stop_reason` value will be `"stop_sequence"` and the response `stop_sequence` value will contain the matched stop sequence. stream: Whether to incrementally stream the response using server-sent events. See [streaming](https://platform.claude.com/docs/en/build-with-claude/streaming) for details. system: System prompt. A system prompt is a way of providing context and instructions to Claude, such as specifying a particular goal or role. See our [guide to system prompts](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role). temperature: Amount of randomness injected into the response. Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` for analytical / multiple choice, and closer to `1.0` for creative and generative tasks. Note that even with `temperature` of `0.0`, the results will not be fully deterministic. thinking: Configuration for enabling Claude's extended thinking. When enabled, responses include `thinking` content blocks showing Claude's thinking process before the final answer. Requires a minimum budget of 1,024 tokens and counts towards your `max_tokens` limit. See [extended thinking](https://platform.claude.com/docs/en/build-with-claude/extended-thinking) for details. tool_choice: How the model should use the provided tools. The model can use a specific tool, any available tool, decide by itself, or not use tools at all. tools: Definitions of tools that the model may use. If you include `tools` in your API request, the model may return `tool_use` content blocks that represent the model's use of those tools. You can then run those tools using the tool input generated by the model and then optionally return results back to the model using `tool_result` content blocks. There are two types of tools: **client tools** and **server tools**. The behavior described below applies to client tools. For [server tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools), see their individual documentation as each has its own behavior (e.g., the [web search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool)). Each tool definition includes: - `name`: Name of the tool. - `description`: Optional, but strongly-recommended description of the tool. - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the tool `input` shape that the model will produce in `tool_use` output content blocks. For example, if you defined `tools` as: ```json [ { "name": "get_stock_price", "description": "Get the current stock price for a given ticker symbol.", "input_schema": { "type": "object", "properties": { "ticker": { "type": "string", "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." } }, "required": ["ticker"] } } ] ``` And then asked the model "What's the S&P 500 at today?", the model might produce `tool_use` content blocks in the response like this: ```json [ { "type": "tool_use", "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "name": "get_stock_price", "input": { "ticker": "^GSPC" } } ] ``` You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an input, and return the following back to the model in a subsequent `user` message: ```json [ { "type": "tool_result", "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "content": "259.75 USD" } ] ``` Tools can be used for workflows that include running client-side tools and functions, or more generally whenever you want the model to produce a particular JSON structure of output. See our [guide](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview) for more details. top_k: Only sample from the top K options for each subsequent token. Used to remove "long tail" low probability responses. [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). Recommended for advanced use cases only. top_p: Use nucleus sampling. In nucleus sampling, we compute the cumulative distribution over all the options for each subsequent token in decreasing probability order and cut it off once it reaches a particular probability specified by `top_p`. Recommended for advanced use cases only. user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a party other than your organization. Requires the `user-profiles` beta header. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ ... @overload def create( self, *, max_tokens: int, messages: Iterable[MessageParam], model: ModelParam, stream: Literal[True], cache_control: Optional[CacheControlEphemeralParam] | Omit = omit, container: Optional[str] | Omit = omit, inference_geo: Optional[str] | Omit = omit, metadata: MetadataParam | Omit = omit, output_config: OutputConfigParam | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, system: Union[str, Iterable[TextBlockParam]] | Omit = omit, temperature: float | Omit = omit, thinking: ThinkingConfigParam | Omit = omit, tool_choice: ToolChoiceParam | Omit = omit, tools: Iterable[ToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Stream[RawMessageStreamEvent]: """ Send a structured list of input messages with text and/or image content, and the model will generate the next message in the conversation. The Messages API can be used for either single queries or stateless multi-turn conversations. Learn more about the Messages API in our [user guide](https://platform.claude.com/docs/en/get-started) Args: max_tokens: The maximum number of tokens to generate before stopping. Note that our models may stop _before_ reaching this maximum. This parameter only specifies the absolute maximum number of tokens to generate. Set to `0` to populate the [prompt cache](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pre-warming-the-cache) without generating a response. Different models have different maximum values for this parameter. See [models](https://platform.claude.com/docs/en/about-claude/models/overview) for details. messages: Input messages. Our models are trained to operate on alternating `user` and `assistant` conversational turns. When creating a new `Message`, you specify the prior conversational turns with the `messages` parameter, and the model then generates the next `Message` in the conversation. Consecutive `user` or `assistant` turns in your request will be combined into a single turn. Each input message must be an object with a `role` and `content`. You can specify a single `user`-role message, or you can include multiple `user` and `assistant` messages. If the final message uses the `assistant` role, the response content will continue immediately from the content in that message. This can be used to constrain part of the model's response. Example with a single `user` message: ```json [{ "role": "user", "content": "Hello, Claude" }] ``` Example with multiple conversational turns: ```json [ { "role": "user", "content": "Hello there." }, { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, { "role": "user", "content": "Can you explain LLMs in plain English?" } ] ``` Example with a partially-filled response from Claude: ```json [ { "role": "user", "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" }, { "role": "assistant", "content": "The best answer is (" } ] ``` Each input message `content` may be either a single `string` or an array of content blocks, where each block has a specific `type`. Using a `string` for `content` is shorthand for an array of one content block of type `"text"`. The following input messages are equivalent: ```json { "role": "user", "content": "Hello, Claude" } ``` ```json { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } ``` See [input examples](https://platform.claude.com/docs/en/build-with-claude/working-with-messages). Note that if you want to include a [system prompt](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role), you can use the top-level `system` parameter — there is no `"system"` role for input messages in the Messages API. There is a limit of 100,000 messages in a single request. model: The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. stream: Whether to incrementally stream the response using server-sent events. See [streaming](https://platform.claude.com/docs/en/build-with-claude/streaming) for details. cache_control: Top-level cache control automatically applies a cache_control marker to the last cacheable block in the request. container: Container identifier for reuse across requests. inference_geo: Specifies the geographic region for inference processing. If not specified, the workspace's `default_inference_geo` is used. metadata: An object describing metadata about the request. output_config: Configuration options for the model's output, such as the output format. service_tier: Determines whether to use priority capacity (if available) or standard capacity for this request. Anthropic offers different levels of service for your API requests. See [service-tiers](https://platform.claude.com/docs/en/api/service-tiers) for details. stop_sequences: Custom text sequences that will cause the model to stop generating. Our models will normally stop when they have naturally completed their turn, which will result in a response `stop_reason` of `"end_turn"`. If you want the model to stop generating when it encounters custom strings of text, you can use the `stop_sequences` parameter. If the model encounters one of the custom sequences, the response `stop_reason` value will be `"stop_sequence"` and the response `stop_sequence` value will contain the matched stop sequence. system: System prompt. A system prompt is a way of providing context and instructions to Claude, such as specifying a particular goal or role. See our [guide to system prompts](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role). temperature: Amount of randomness injected into the response. Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` for analytical / multiple choice, and closer to `1.0` for creative and generative tasks. Note that even with `temperature` of `0.0`, the results will not be fully deterministic. thinking: Configuration for enabling Claude's extended thinking. When enabled, responses include `thinking` content blocks showing Claude's thinking process before the final answer. Requires a minimum budget of 1,024 tokens and counts towards your `max_tokens` limit. See [extended thinking](https://platform.claude.com/docs/en/build-with-claude/extended-thinking) for details. tool_choice: How the model should use the provided tools. The model can use a specific tool, any available tool, decide by itself, or not use tools at all. tools: Definitions of tools that the model may use. If you include `tools` in your API request, the model may return `tool_use` content blocks that represent the model's use of those tools. You can then run those tools using the tool input generated by the model and then optionally return results back to the model using `tool_result` content blocks. There are two types of tools: **client tools** and **server tools**. The behavior described below applies to client tools. For [server tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools), see their individual documentation as each has its own behavior (e.g., the [web search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool)). Each tool definition includes: - `name`: Name of the tool. - `description`: Optional, but strongly-recommended description of the tool. - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the tool `input` shape that the model will produce in `tool_use` output content blocks. For example, if you defined `tools` as: ```json [ { "name": "get_stock_price", "description": "Get the current stock price for a given ticker symbol.", "input_schema": { "type": "object", "properties": { "ticker": { "type": "string", "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." } }, "required": ["ticker"] } } ] ``` And then asked the model "What's the S&P 500 at today?", the model might produce `tool_use` content blocks in the response like this: ```json [ { "type": "tool_use", "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "name": "get_stock_price", "input": { "ticker": "^GSPC" } } ] ``` You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an input, and return the following back to the model in a subsequent `user` message: ```json [ { "type": "tool_result", "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "content": "259.75 USD" } ] ``` Tools can be used for workflows that include running client-side tools and functions, or more generally whenever you want the model to produce a particular JSON structure of output. See our [guide](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview) for more details. top_k: Only sample from the top K options for each subsequent token. Used to remove "long tail" low probability responses. [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). Recommended for advanced use cases only. top_p: Use nucleus sampling. In nucleus sampling, we compute the cumulative distribution over all the options for each subsequent token in decreasing probability order and cut it off once it reaches a particular probability specified by `top_p`. Recommended for advanced use cases only. user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a party other than your organization. Requires the `user-profiles` beta header. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ ... @overload def create( self, *, max_tokens: int, messages: Iterable[MessageParam], model: ModelParam, stream: bool, cache_control: Optional[CacheControlEphemeralParam] | Omit = omit, container: Optional[str] | Omit = omit, inference_geo: Optional[str] | Omit = omit, metadata: MetadataParam | Omit = omit, output_config: OutputConfigParam | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, system: Union[str, Iterable[TextBlockParam]] | Omit = omit, temperature: float | Omit = omit, thinking: ThinkingConfigParam | Omit = omit, tool_choice: ToolChoiceParam | Omit = omit, tools: Iterable[ToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Message | Stream[RawMessageStreamEvent]: """ Send a structured list of input messages with text and/or image content, and the model will generate the next message in the conversation. The Messages API can be used for either single queries or stateless multi-turn conversations. Learn more about the Messages API in our [user guide](https://platform.claude.com/docs/en/get-started) Args: max_tokens: The maximum number of tokens to generate before stopping. Note that our models may stop _before_ reaching this maximum. This parameter only specifies the absolute maximum number of tokens to generate. Set to `0` to populate the [prompt cache](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pre-warming-the-cache) without generating a response. Different models have different maximum values for this parameter. See [models](https://platform.claude.com/docs/en/about-claude/models/overview) for details. messages: Input messages. Our models are trained to operate on alternating `user` and `assistant` conversational turns. When creating a new `Message`, you specify the prior conversational turns with the `messages` parameter, and the model then generates the next `Message` in the conversation. Consecutive `user` or `assistant` turns in your request will be combined into a single turn. Each input message must be an object with a `role` and `content`. You can specify a single `user`-role message, or you can include multiple `user` and `assistant` messages. If the final message uses the `assistant` role, the response content will continue immediately from the content in that message. This can be used to constrain part of the model's response. Example with a single `user` message: ```json [{ "role": "user", "content": "Hello, Claude" }] ``` Example with multiple conversational turns: ```json [ { "role": "user", "content": "Hello there." }, { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, { "role": "user", "content": "Can you explain LLMs in plain English?" } ] ``` Example with a partially-filled response from Claude: ```json [ { "role": "user", "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" }, { "role": "assistant", "content": "The best answer is (" } ] ``` Each input message `content` may be either a single `string` or an array of content blocks, where each block has a specific `type`. Using a `string` for `content` is shorthand for an array of one content block of type `"text"`. The following input messages are equivalent: ```json { "role": "user", "content": "Hello, Claude" } ``` ```json { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } ``` See [input examples](https://platform.claude.com/docs/en/build-with-claude/working-with-messages). Note that if you want to include a [system prompt](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role), you can use the top-level `system` parameter — there is no `"system"` role for input messages in the Messages API. There is a limit of 100,000 messages in a single request. model: The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. stream: Whether to incrementally stream the response using server-sent events. See [streaming](https://platform.claude.com/docs/en/build-with-claude/streaming) for details. cache_control: Top-level cache control automatically applies a cache_control marker to the last cacheable block in the request. container: Container identifier for reuse across requests. inference_geo: Specifies the geographic region for inference processing. If not specified, the workspace's `default_inference_geo` is used. metadata: An object describing metadata about the request. output_config: Configuration options for the model's output, such as the output format. service_tier: Determines whether to use priority capacity (if available) or standard capacity for this request. Anthropic offers different levels of service for your API requests. See [service-tiers](https://platform.claude.com/docs/en/api/service-tiers) for details. stop_sequences: Custom text sequences that will cause the model to stop generating. Our models will normally stop when they have naturally completed their turn, which will result in a response `stop_reason` of `"end_turn"`. If you want the model to stop generating when it encounters custom strings of text, you can use the `stop_sequences` parameter. If the model encounters one of the custom sequences, the response `stop_reason` value will be `"stop_sequence"` and the response `stop_sequence` value will contain the matched stop sequence. system: System prompt. A system prompt is a way of providing context and instructions to Claude, such as specifying a particular goal or role. See our [guide to system prompts](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role). temperature: Amount of randomness injected into the response. Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` for analytical / multiple choice, and closer to `1.0` for creative and generative tasks. Note that even with `temperature` of `0.0`, the results will not be fully deterministic. thinking: Configuration for enabling Claude's extended thinking. When enabled, responses include `thinking` content blocks showing Claude's thinking process before the final answer. Requires a minimum budget of 1,024 tokens and counts towards your `max_tokens` limit. See [extended thinking](https://platform.claude.com/docs/en/build-with-claude/extended-thinking) for details. tool_choice: How the model should use the provided tools. The model can use a specific tool, any available tool, decide by itself, or not use tools at all. tools: Definitions of tools that the model may use. If you include `tools` in your API request, the model may return `tool_use` content blocks that represent the model's use of those tools. You can then run those tools using the tool input generated by the model and then optionally return results back to the model using `tool_result` content blocks. There are two types of tools: **client tools** and **server tools**. The behavior described below applies to client tools. For [server tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools), see their individual documentation as each has its own behavior (e.g., the [web search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool)). Each tool definition includes: - `name`: Name of the tool. - `description`: Optional, but strongly-recommended description of the tool. - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the tool `input` shape that the model will produce in `tool_use` output content blocks. For example, if you defined `tools` as: ```json [ { "name": "get_stock_price", "description": "Get the current stock price for a given ticker symbol.", "input_schema": { "type": "object", "properties": { "ticker": { "type": "string", "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." } }, "required": ["ticker"] } } ] ``` And then asked the model "What's the S&P 500 at today?", the model might produce `tool_use` content blocks in the response like this: ```json [ { "type": "tool_use", "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "name": "get_stock_price", "input": { "ticker": "^GSPC" } } ] ``` You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an input, and return the following back to the model in a subsequent `user` message: ```json [ { "type": "tool_result", "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "content": "259.75 USD" } ] ``` Tools can be used for workflows that include running client-side tools and functions, or more generally whenever you want the model to produce a particular JSON structure of output. See our [guide](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview) for more details. top_k: Only sample from the top K options for each subsequent token. Used to remove "long tail" low probability responses. [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). Recommended for advanced use cases only. top_p: Use nucleus sampling. In nucleus sampling, we compute the cumulative distribution over all the options for each subsequent token in decreasing probability order and cut it off once it reaches a particular probability specified by `top_p`. Recommended for advanced use cases only. user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a party other than your organization. Requires the `user-profiles` beta header. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ ... @required_args(["max_tokens", "messages", "model"], ["max_tokens", "messages", "model", "stream"]) def create( self, *, max_tokens: int, messages: Iterable[MessageParam], model: ModelParam, cache_control: Optional[CacheControlEphemeralParam] | Omit = omit, container: Optional[str] | Omit = omit, inference_geo: Optional[str] | Omit = omit, metadata: MetadataParam | Omit = omit, output_config: OutputConfigParam | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, stream: Literal[False] | Literal[True] | Omit = omit, system: Union[str, Iterable[TextBlockParam]] | Omit = omit, temperature: float | Omit = omit, thinking: ThinkingConfigParam | Omit = omit, tool_choice: ToolChoiceParam | Omit = omit, tools: Iterable[ToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Message | Stream[RawMessageStreamEvent]: if not stream and not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: timeout = self._client._calculate_nonstreaming_timeout( max_tokens, MODEL_NONSTREAMING_TOKENS.get(model, None) ) if model in DEPRECATED_MODELS: warnings.warn( f"The model '{model}' is deprecated and will reach end-of-life on {DEPRECATED_MODELS[model]}.\nPlease migrate to a newer model. Visit https://docs.anthropic.com/en/docs/resources/model-deprecations for more information.", DeprecationWarning, stacklevel=3, ) if model in MODELS_TO_WARN_WITH_THINKING_ENABLED and thinking and thinking["type"] == "enabled": warnings.warn( f"Using Claude with {model} and 'thinking.type=enabled' is deprecated. Use 'thinking.type=adaptive' instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking", UserWarning, stacklevel=3, ) extra_headers = {**strip_not_given({"anthropic-user-profile-id": user_profile_id}), **(extra_headers or {})} return self._post( "/v1/messages", body=maybe_transform( { "max_tokens": max_tokens, "messages": messages, "model": model, "cache_control": cache_control, "container": container, "inference_geo": inference_geo, "metadata": metadata, "output_config": output_config, "service_tier": service_tier, "stop_sequences": stop_sequences, "stream": stream, "system": system, "temperature": temperature, "thinking": thinking, "tool_choice": tool_choice, "tools": tools, "top_k": top_k, "top_p": top_p, }, message_create_params.MessageCreateParamsStreaming if stream else message_create_params.MessageCreateParamsNonStreaming, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=Message, stream=stream or False, stream_cls=Stream[RawMessageStreamEvent], ) def stream( self, *, max_tokens: int, messages: Iterable[MessageParam], model: ModelParam, cache_control: Optional[CacheControlEphemeralParam] | Omit = omit, inference_geo: Optional[str] | Omit = omit, metadata: MetadataParam | Omit = omit, output_config: OutputConfigParam | Omit = omit, output_format: None | JSONOutputFormatParam | type[ResponseFormatT] | Omit = omit, container: Optional[str] | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, system: Union[str, Iterable[TextBlockParam]] | Omit = omit, temperature: float | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, thinking: ThinkingConfigParam | Omit = omit, tool_choice: ToolChoiceParam | Omit = omit, tools: Iterable[ToolUnionParam] | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> MessageStreamManager[ResponseFormatT]: """Create a Message stream""" if model in DEPRECATED_MODELS: warnings.warn( f"The model '{model}' is deprecated and will reach end-of-life on {DEPRECATED_MODELS[model]}.\nPlease migrate to a newer model. Visit https://docs.anthropic.com/en/docs/resources/model-deprecations for more information.", DeprecationWarning, stacklevel=3, ) if model in MODELS_TO_WARN_WITH_THINKING_ENABLED and thinking and thinking["type"] == "enabled": warnings.warn( f"Using Claude with {model} and 'thinking.type=enabled' is deprecated. Use 'thinking.type=adaptive' instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking", UserWarning, stacklevel=3, ) extra_headers = { **strip_not_given({"anthropic-user-profile-id": user_profile_id}), _STAINLESS_HELPER_METHOD_HEADER: _HELPER_METHOD_STREAM, _STAINLESS_STREAM_HELPER_HEADER: "messages", **(extra_headers or {}), } transformed_output_format: Optional[JSONOutputFormatParam] | NotGiven = NOT_GIVEN if is_dict(output_format): transformed_output_format = cast(JSONOutputFormatParam, output_format) elif is_given(output_format) and output_format is not None: adapted_type: TypeAdapter[ResponseFormatT] = TypeAdapter(output_format) try: schema = adapted_type.json_schema() transformed_output_format = JSONOutputFormatParam(schema=transform_schema(schema), type="json_schema") except pydantic.errors.PydanticSchemaGenerationError as e: raise TypeError( ( "Could not generate JSON schema for the given `output_format` type. " "Use a type that works with `pydantic.TypeAdapter`" ) ) from e # Merge output_format into output_config merged_output_config: OutputConfigParam | Omit = omit if is_given(transformed_output_format): if is_given(output_config): merged_output_config = {**output_config, "format": transformed_output_format} else: merged_output_config = {"format": transformed_output_format} elif is_given(output_config): merged_output_config = output_config make_request = partial( self._post, "/v1/messages", body=maybe_transform( { "max_tokens": max_tokens, "messages": messages, "model": model, "cache_control": cache_control, "inference_geo": inference_geo, "metadata": metadata, "output_config": merged_output_config, "container": container, "service_tier": service_tier, "stop_sequences": stop_sequences, "system": system, "temperature": temperature, "top_k": top_k, "top_p": top_p, "tools": tools, "thinking": thinking, "tool_choice": tool_choice, "stream": True, }, message_create_params.MessageCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=Message, stream=True, stream_cls=Stream[RawMessageStreamEvent], ) return MessageStreamManager( make_request, output_format=NOT_GIVEN if is_dict(output_format) else cast(ResponseFormatT, output_format), ) def parse( self, *, max_tokens: int, messages: Iterable[MessageParam], model: ModelParam, metadata: MetadataParam | Omit = omit, output_config: OutputConfigParam | Omit = omit, output_format: Optional[type[ResponseFormatT]] | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, stream: Literal[False] | Literal[True] | Omit = omit, system: Union[str, Iterable[TextBlockParam]] | Omit = omit, temperature: float | Omit = omit, thinking: ThinkingConfigParam | Omit = omit, tool_choice: ToolChoiceParam | Omit = omit, tools: Iterable[ToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ParsedMessage[ResponseFormatT]: if not stream and not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: timeout = self._client._calculate_nonstreaming_timeout( max_tokens, MODEL_NONSTREAMING_TOKENS.get(model, None) ) if model in DEPRECATED_MODELS: warnings.warn( f"The model '{model}' is deprecated and will reach end-of-life on {DEPRECATED_MODELS[model]}.\nPlease migrate to a newer model. Visit https://docs.anthropic.com/en/docs/resources/model-deprecations for more information.", DeprecationWarning, stacklevel=3, ) if model in MODELS_TO_WARN_WITH_THINKING_ENABLED and thinking and thinking["type"] == "enabled": warnings.warn( f"Using Claude with {model} and 'thinking.type=enabled' is deprecated. Use 'thinking.type=adaptive' instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking", UserWarning, stacklevel=3, ) extra_headers = merge_headers( _helper_header("messages.parse"), extra_headers or {}, ) transformed_output_format: Optional[JSONOutputFormatParam] | NotGiven = NOT_GIVEN if is_given(output_format) and output_format is not None: adapted_type: TypeAdapter[ResponseFormatT] = TypeAdapter(output_format) try: schema = adapted_type.json_schema() transformed_output_format = JSONOutputFormatParam(schema=transform_schema(schema), type="json_schema") except pydantic.errors.PydanticSchemaGenerationError as e: raise TypeError( ( "Could not generate JSON schema for the given `output_format` type. " "Use a type that works with `pydantic.TypeAdapter`" ) ) from e def parser(response: Message) -> ParsedMessage[ResponseFormatT]: return parse_response( response=response, output_format=cast( ResponseFormatT, output_format if is_given(output_format) and output_format is not None else NOT_GIVEN, ), ) # Merge output_format into output_config merged_output_config: OutputConfigParam | Omit = omit if is_given(transformed_output_format): if is_given(output_config): merged_output_config = {**output_config, "format": transformed_output_format} else: merged_output_config = {"format": transformed_output_format} elif is_given(output_config): merged_output_config = output_config return self._post( "/v1/messages", body=maybe_transform( { "max_tokens": max_tokens, "messages": messages, "model": model, "metadata": metadata, "output_config": merged_output_config, "service_tier": service_tier, "stop_sequences": stop_sequences, "stream": stream, "system": system, "temperature": temperature, "thinking": thinking, "tool_choice": tool_choice, "tools": tools, "top_k": top_k, "top_p": top_p, }, message_create_params.MessageCreateParamsNonStreaming, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, post_parser=parser, ), cast_to=cast(Type[ParsedMessage[ResponseFormatT]], Message), stream=False, ) def count_tokens( self, *, messages: Iterable[MessageParam], model: ModelParam, cache_control: Optional[CacheControlEphemeralParam] | Omit = omit, output_config: OutputConfigParam | Omit = omit, output_format: None | JSONOutputFormatParam | type | Omit = omit, system: Union[str, Iterable[TextBlockParam]] | Omit = omit, thinking: ThinkingConfigParam | Omit = omit, tool_choice: ToolChoiceParam | Omit = omit, tools: Iterable[MessageCountTokensToolParam] | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> MessageTokensCount: """ Count the number of tokens in a Message. The Token Count API can be used to count the number of tokens in a Message, including tools, images, and documents, without creating it. Learn more about token counting in our [user guide](https://platform.claude.com/docs/en/build-with-claude/token-counting) Args: messages: Input messages. Our models are trained to operate on alternating `user` and `assistant` conversational turns. When creating a new `Message`, you specify the prior conversational turns with the `messages` parameter, and the model then generates the next `Message` in the conversation. Consecutive `user` or `assistant` turns in your request will be combined into a single turn. Each input message must be an object with a `role` and `content`. You can specify a single `user`-role message, or you can include multiple `user` and `assistant` messages. If the final message uses the `assistant` role, the response content will continue immediately from the content in that message. This can be used to constrain part of the model's response. Example with a single `user` message: ```json [{ "role": "user", "content": "Hello, Claude" }] ``` Example with multiple conversational turns: ```json [ { "role": "user", "content": "Hello there." }, { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, { "role": "user", "content": "Can you explain LLMs in plain English?" } ] ``` Example with a partially-filled response from Claude: ```json [ { "role": "user", "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" }, { "role": "assistant", "content": "The best answer is (" } ] ``` Each input message `content` may be either a single `string` or an array of content blocks, where each block has a specific `type`. Using a `string` for `content` is shorthand for an array of one content block of type `"text"`. The following input messages are equivalent: ```json { "role": "user", "content": "Hello, Claude" } ``` ```json { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } ``` See [input examples](https://platform.claude.com/docs/en/build-with-claude/working-with-messages). Note that if you want to include a [system prompt](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role), you can use the top-level `system` parameter — there is no `"system"` role for input messages in the Messages API. There is a limit of 100,000 messages in a single request. model: The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. cache_control: Top-level cache control automatically applies a cache_control marker to the last cacheable block in the request. output_config: Configuration options for the model's output, such as the output format. system: System prompt. A system prompt is a way of providing context and instructions to Claude, such as specifying a particular goal or role. See our [guide to system prompts](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role). thinking: Configuration for enabling Claude's extended thinking. When enabled, responses include `thinking` content blocks showing Claude's thinking process before the final answer. Requires a minimum budget of 1,024 tokens and counts towards your `max_tokens` limit. See [extended thinking](https://platform.claude.com/docs/en/build-with-claude/extended-thinking) for details. tool_choice: How the model should use the provided tools. The model can use a specific tool, any available tool, decide by itself, or not use tools at all. tools: Definitions of tools that the model may use. If you include `tools` in your API request, the model may return `tool_use` content blocks that represent the model's use of those tools. You can then run those tools using the tool input generated by the model and then optionally return results back to the model using `tool_result` content blocks. There are two types of tools: **client tools** and **server tools**. The behavior described below applies to client tools. For [server tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools), see their individual documentation as each has its own behavior (e.g., the [web search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool)). Each tool definition includes: - `name`: Name of the tool. - `description`: Optional, but strongly-recommended description of the tool. - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the tool `input` shape that the model will produce in `tool_use` output content blocks. For example, if you defined `tools` as: ```json [ { "name": "get_stock_price", "description": "Get the current stock price for a given ticker symbol.", "input_schema": { "type": "object", "properties": { "ticker": { "type": "string", "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." } }, "required": ["ticker"] } } ] ``` And then asked the model "What's the S&P 500 at today?", the model might produce `tool_use` content blocks in the response like this: ```json [ { "type": "tool_use", "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "name": "get_stock_price", "input": { "ticker": "^GSPC" } } ] ``` You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an input, and return the following back to the model in a subsequent `user` message: ```json [ { "type": "tool_result", "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "content": "259.75 USD" } ] ``` Tools can be used for workflows that include running client-side tools and functions, or more generally whenever you want the model to produce a particular JSON structure of output. See our [guide](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview) for more details. user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a party other than your organization. Requires the `user-profiles` beta header. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = {**strip_not_given({"anthropic-user-profile-id": user_profile_id}), **(extra_headers or {})} # Transform output_format if provided transformed_output_format: Optional[JSONOutputFormatParam] | NotGiven = NOT_GIVEN if is_dict(output_format): transformed_output_format = cast(JSONOutputFormatParam, output_format) elif is_given(output_format) and output_format is not None: adapted_type: TypeAdapter[type] = TypeAdapter(output_format) try: schema = adapted_type.json_schema() transformed_output_format = JSONOutputFormatParam(schema=transform_schema(schema), type="json_schema") except pydantic.errors.PydanticSchemaGenerationError as e: raise TypeError( ( "Could not generate JSON schema for the given `output_format` type. " "Use a type that works with `pydantic.TypeAdapter`" ) ) from e # Merge output_format into output_config merged_output_config: OutputConfigParam | Omit = omit if is_given(transformed_output_format): if is_given(output_config): merged_output_config = {**output_config, "format": transformed_output_format} else: merged_output_config = {"format": transformed_output_format} elif is_given(output_config): merged_output_config = output_config return self._post( "/v1/messages/count_tokens", body=maybe_transform( { "messages": messages, "model": model, "cache_control": cache_control, "output_config": merged_output_config, "system": system, "thinking": thinking, "tool_choice": tool_choice, "tools": tools, }, message_count_tokens_params.MessageCountTokensParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=MessageTokensCount, ) class AsyncMessages(AsyncAPIResource): @cached_property def batches(self) -> AsyncBatches: return AsyncBatches(self._client) @cached_property def with_raw_response(self) -> AsyncMessagesWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncMessagesWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncMessagesWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncMessagesWithStreamingResponse(self) @overload async def create( self, *, max_tokens: int, messages: Iterable[MessageParam], model: ModelParam, cache_control: Optional[CacheControlEphemeralParam] | Omit = omit, container: Optional[str] | Omit = omit, inference_geo: Optional[str] | Omit = omit, metadata: MetadataParam | Omit = omit, output_config: OutputConfigParam | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, stream: Literal[False] | Omit = omit, system: Union[str, Iterable[TextBlockParam]] | Omit = omit, temperature: float | Omit = omit, thinking: ThinkingConfigParam | Omit = omit, tool_choice: ToolChoiceParam | Omit = omit, tools: Iterable[ToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Message: """ Send a structured list of input messages with text and/or image content, and the model will generate the next message in the conversation. The Messages API can be used for either single queries or stateless multi-turn conversations. Learn more about the Messages API in our [user guide](https://platform.claude.com/docs/en/get-started) Args: max_tokens: The maximum number of tokens to generate before stopping. Note that our models may stop _before_ reaching this maximum. This parameter only specifies the absolute maximum number of tokens to generate. Set to `0` to populate the [prompt cache](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pre-warming-the-cache) without generating a response. Different models have different maximum values for this parameter. See [models](https://platform.claude.com/docs/en/about-claude/models/overview) for details. messages: Input messages. Our models are trained to operate on alternating `user` and `assistant` conversational turns. When creating a new `Message`, you specify the prior conversational turns with the `messages` parameter, and the model then generates the next `Message` in the conversation. Consecutive `user` or `assistant` turns in your request will be combined into a single turn. Each input message must be an object with a `role` and `content`. You can specify a single `user`-role message, or you can include multiple `user` and `assistant` messages. If the final message uses the `assistant` role, the response content will continue immediately from the content in that message. This can be used to constrain part of the model's response. Example with a single `user` message: ```json [{ "role": "user", "content": "Hello, Claude" }] ``` Example with multiple conversational turns: ```json [ { "role": "user", "content": "Hello there." }, { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, { "role": "user", "content": "Can you explain LLMs in plain English?" } ] ``` Example with a partially-filled response from Claude: ```json [ { "role": "user", "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" }, { "role": "assistant", "content": "The best answer is (" } ] ``` Each input message `content` may be either a single `string` or an array of content blocks, where each block has a specific `type`. Using a `string` for `content` is shorthand for an array of one content block of type `"text"`. The following input messages are equivalent: ```json { "role": "user", "content": "Hello, Claude" } ``` ```json { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } ``` See [input examples](https://platform.claude.com/docs/en/build-with-claude/working-with-messages). Note that if you want to include a [system prompt](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role), you can use the top-level `system` parameter — there is no `"system"` role for input messages in the Messages API. There is a limit of 100,000 messages in a single request. model: The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. cache_control: Top-level cache control automatically applies a cache_control marker to the last cacheable block in the request. container: Container identifier for reuse across requests. inference_geo: Specifies the geographic region for inference processing. If not specified, the workspace's `default_inference_geo` is used. metadata: An object describing metadata about the request. output_config: Configuration options for the model's output, such as the output format. service_tier: Determines whether to use priority capacity (if available) or standard capacity for this request. Anthropic offers different levels of service for your API requests. See [service-tiers](https://platform.claude.com/docs/en/api/service-tiers) for details. stop_sequences: Custom text sequences that will cause the model to stop generating. Our models will normally stop when they have naturally completed their turn, which will result in a response `stop_reason` of `"end_turn"`. If you want the model to stop generating when it encounters custom strings of text, you can use the `stop_sequences` parameter. If the model encounters one of the custom sequences, the response `stop_reason` value will be `"stop_sequence"` and the response `stop_sequence` value will contain the matched stop sequence. stream: Whether to incrementally stream the response using server-sent events. See [streaming](https://platform.claude.com/docs/en/build-with-claude/streaming) for details. system: System prompt. A system prompt is a way of providing context and instructions to Claude, such as specifying a particular goal or role. See our [guide to system prompts](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role). temperature: Amount of randomness injected into the response. Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` for analytical / multiple choice, and closer to `1.0` for creative and generative tasks. Note that even with `temperature` of `0.0`, the results will not be fully deterministic. thinking: Configuration for enabling Claude's extended thinking. When enabled, responses include `thinking` content blocks showing Claude's thinking process before the final answer. Requires a minimum budget of 1,024 tokens and counts towards your `max_tokens` limit. See [extended thinking](https://platform.claude.com/docs/en/build-with-claude/extended-thinking) for details. tool_choice: How the model should use the provided tools. The model can use a specific tool, any available tool, decide by itself, or not use tools at all. tools: Definitions of tools that the model may use. If you include `tools` in your API request, the model may return `tool_use` content blocks that represent the model's use of those tools. You can then run those tools using the tool input generated by the model and then optionally return results back to the model using `tool_result` content blocks. There are two types of tools: **client tools** and **server tools**. The behavior described below applies to client tools. For [server tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools), see their individual documentation as each has its own behavior (e.g., the [web search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool)). Each tool definition includes: - `name`: Name of the tool. - `description`: Optional, but strongly-recommended description of the tool. - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the tool `input` shape that the model will produce in `tool_use` output content blocks. For example, if you defined `tools` as: ```json [ { "name": "get_stock_price", "description": "Get the current stock price for a given ticker symbol.", "input_schema": { "type": "object", "properties": { "ticker": { "type": "string", "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." } }, "required": ["ticker"] } } ] ``` And then asked the model "What's the S&P 500 at today?", the model might produce `tool_use` content blocks in the response like this: ```json [ { "type": "tool_use", "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "name": "get_stock_price", "input": { "ticker": "^GSPC" } } ] ``` You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an input, and return the following back to the model in a subsequent `user` message: ```json [ { "type": "tool_result", "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "content": "259.75 USD" } ] ``` Tools can be used for workflows that include running client-side tools and functions, or more generally whenever you want the model to produce a particular JSON structure of output. See our [guide](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview) for more details. top_k: Only sample from the top K options for each subsequent token. Used to remove "long tail" low probability responses. [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). Recommended for advanced use cases only. top_p: Use nucleus sampling. In nucleus sampling, we compute the cumulative distribution over all the options for each subsequent token in decreasing probability order and cut it off once it reaches a particular probability specified by `top_p`. Recommended for advanced use cases only. user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a party other than your organization. Requires the `user-profiles` beta header. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ ... @overload async def create( self, *, max_tokens: int, messages: Iterable[MessageParam], model: ModelParam, stream: Literal[True], cache_control: Optional[CacheControlEphemeralParam] | Omit = omit, container: Optional[str] | Omit = omit, inference_geo: Optional[str] | Omit = omit, metadata: MetadataParam | Omit = omit, output_config: OutputConfigParam | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, system: Union[str, Iterable[TextBlockParam]] | Omit = omit, temperature: float | Omit = omit, thinking: ThinkingConfigParam | Omit = omit, tool_choice: ToolChoiceParam | Omit = omit, tools: Iterable[ToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncStream[RawMessageStreamEvent]: """ Send a structured list of input messages with text and/or image content, and the model will generate the next message in the conversation. The Messages API can be used for either single queries or stateless multi-turn conversations. Learn more about the Messages API in our [user guide](https://platform.claude.com/docs/en/get-started) Args: max_tokens: The maximum number of tokens to generate before stopping. Note that our models may stop _before_ reaching this maximum. This parameter only specifies the absolute maximum number of tokens to generate. Set to `0` to populate the [prompt cache](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pre-warming-the-cache) without generating a response. Different models have different maximum values for this parameter. See [models](https://platform.claude.com/docs/en/about-claude/models/overview) for details. messages: Input messages. Our models are trained to operate on alternating `user` and `assistant` conversational turns. When creating a new `Message`, you specify the prior conversational turns with the `messages` parameter, and the model then generates the next `Message` in the conversation. Consecutive `user` or `assistant` turns in your request will be combined into a single turn. Each input message must be an object with a `role` and `content`. You can specify a single `user`-role message, or you can include multiple `user` and `assistant` messages. If the final message uses the `assistant` role, the response content will continue immediately from the content in that message. This can be used to constrain part of the model's response. Example with a single `user` message: ```json [{ "role": "user", "content": "Hello, Claude" }] ``` Example with multiple conversational turns: ```json [ { "role": "user", "content": "Hello there." }, { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, { "role": "user", "content": "Can you explain LLMs in plain English?" } ] ``` Example with a partially-filled response from Claude: ```json [ { "role": "user", "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" }, { "role": "assistant", "content": "The best answer is (" } ] ``` Each input message `content` may be either a single `string` or an array of content blocks, where each block has a specific `type`. Using a `string` for `content` is shorthand for an array of one content block of type `"text"`. The following input messages are equivalent: ```json { "role": "user", "content": "Hello, Claude" } ``` ```json { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } ``` See [input examples](https://platform.claude.com/docs/en/build-with-claude/working-with-messages). Note that if you want to include a [system prompt](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role), you can use the top-level `system` parameter — there is no `"system"` role for input messages in the Messages API. There is a limit of 100,000 messages in a single request. model: The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. stream: Whether to incrementally stream the response using server-sent events. See [streaming](https://platform.claude.com/docs/en/build-with-claude/streaming) for details. cache_control: Top-level cache control automatically applies a cache_control marker to the last cacheable block in the request. container: Container identifier for reuse across requests. inference_geo: Specifies the geographic region for inference processing. If not specified, the workspace's `default_inference_geo` is used. metadata: An object describing metadata about the request. output_config: Configuration options for the model's output, such as the output format. service_tier: Determines whether to use priority capacity (if available) or standard capacity for this request. Anthropic offers different levels of service for your API requests. See [service-tiers](https://platform.claude.com/docs/en/api/service-tiers) for details. stop_sequences: Custom text sequences that will cause the model to stop generating. Our models will normally stop when they have naturally completed their turn, which will result in a response `stop_reason` of `"end_turn"`. If you want the model to stop generating when it encounters custom strings of text, you can use the `stop_sequences` parameter. If the model encounters one of the custom sequences, the response `stop_reason` value will be `"stop_sequence"` and the response `stop_sequence` value will contain the matched stop sequence. system: System prompt. A system prompt is a way of providing context and instructions to Claude, such as specifying a particular goal or role. See our [guide to system prompts](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role). temperature: Amount of randomness injected into the response. Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` for analytical / multiple choice, and closer to `1.0` for creative and generative tasks. Note that even with `temperature` of `0.0`, the results will not be fully deterministic. thinking: Configuration for enabling Claude's extended thinking. When enabled, responses include `thinking` content blocks showing Claude's thinking process before the final answer. Requires a minimum budget of 1,024 tokens and counts towards your `max_tokens` limit. See [extended thinking](https://platform.claude.com/docs/en/build-with-claude/extended-thinking) for details. tool_choice: How the model should use the provided tools. The model can use a specific tool, any available tool, decide by itself, or not use tools at all. tools: Definitions of tools that the model may use. If you include `tools` in your API request, the model may return `tool_use` content blocks that represent the model's use of those tools. You can then run those tools using the tool input generated by the model and then optionally return results back to the model using `tool_result` content blocks. There are two types of tools: **client tools** and **server tools**. The behavior described below applies to client tools. For [server tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools), see their individual documentation as each has its own behavior (e.g., the [web search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool)). Each tool definition includes: - `name`: Name of the tool. - `description`: Optional, but strongly-recommended description of the tool. - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the tool `input` shape that the model will produce in `tool_use` output content blocks. For example, if you defined `tools` as: ```json [ { "name": "get_stock_price", "description": "Get the current stock price for a given ticker symbol.", "input_schema": { "type": "object", "properties": { "ticker": { "type": "string", "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." } }, "required": ["ticker"] } } ] ``` And then asked the model "What's the S&P 500 at today?", the model might produce `tool_use` content blocks in the response like this: ```json [ { "type": "tool_use", "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "name": "get_stock_price", "input": { "ticker": "^GSPC" } } ] ``` You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an input, and return the following back to the model in a subsequent `user` message: ```json [ { "type": "tool_result", "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "content": "259.75 USD" } ] ``` Tools can be used for workflows that include running client-side tools and functions, or more generally whenever you want the model to produce a particular JSON structure of output. See our [guide](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview) for more details. top_k: Only sample from the top K options for each subsequent token. Used to remove "long tail" low probability responses. [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). Recommended for advanced use cases only. top_p: Use nucleus sampling. In nucleus sampling, we compute the cumulative distribution over all the options for each subsequent token in decreasing probability order and cut it off once it reaches a particular probability specified by `top_p`. Recommended for advanced use cases only. user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a party other than your organization. Requires the `user-profiles` beta header. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ ... @overload async def create( self, *, max_tokens: int, messages: Iterable[MessageParam], model: ModelParam, stream: bool, cache_control: Optional[CacheControlEphemeralParam] | Omit = omit, container: Optional[str] | Omit = omit, inference_geo: Optional[str] | Omit = omit, metadata: MetadataParam | Omit = omit, output_config: OutputConfigParam | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, system: Union[str, Iterable[TextBlockParam]] | Omit = omit, temperature: float | Omit = omit, thinking: ThinkingConfigParam | Omit = omit, tool_choice: ToolChoiceParam | Omit = omit, tools: Iterable[ToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Message | AsyncStream[RawMessageStreamEvent]: """ Send a structured list of input messages with text and/or image content, and the model will generate the next message in the conversation. The Messages API can be used for either single queries or stateless multi-turn conversations. Learn more about the Messages API in our [user guide](https://platform.claude.com/docs/en/get-started) Args: max_tokens: The maximum number of tokens to generate before stopping. Note that our models may stop _before_ reaching this maximum. This parameter only specifies the absolute maximum number of tokens to generate. Set to `0` to populate the [prompt cache](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pre-warming-the-cache) without generating a response. Different models have different maximum values for this parameter. See [models](https://platform.claude.com/docs/en/about-claude/models/overview) for details. messages: Input messages. Our models are trained to operate on alternating `user` and `assistant` conversational turns. When creating a new `Message`, you specify the prior conversational turns with the `messages` parameter, and the model then generates the next `Message` in the conversation. Consecutive `user` or `assistant` turns in your request will be combined into a single turn. Each input message must be an object with a `role` and `content`. You can specify a single `user`-role message, or you can include multiple `user` and `assistant` messages. If the final message uses the `assistant` role, the response content will continue immediately from the content in that message. This can be used to constrain part of the model's response. Example with a single `user` message: ```json [{ "role": "user", "content": "Hello, Claude" }] ``` Example with multiple conversational turns: ```json [ { "role": "user", "content": "Hello there." }, { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, { "role": "user", "content": "Can you explain LLMs in plain English?" } ] ``` Example with a partially-filled response from Claude: ```json [ { "role": "user", "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" }, { "role": "assistant", "content": "The best answer is (" } ] ``` Each input message `content` may be either a single `string` or an array of content blocks, where each block has a specific `type`. Using a `string` for `content` is shorthand for an array of one content block of type `"text"`. The following input messages are equivalent: ```json { "role": "user", "content": "Hello, Claude" } ``` ```json { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } ``` See [input examples](https://platform.claude.com/docs/en/build-with-claude/working-with-messages). Note that if you want to include a [system prompt](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role), you can use the top-level `system` parameter — there is no `"system"` role for input messages in the Messages API. There is a limit of 100,000 messages in a single request. model: The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. stream: Whether to incrementally stream the response using server-sent events. See [streaming](https://platform.claude.com/docs/en/build-with-claude/streaming) for details. cache_control: Top-level cache control automatically applies a cache_control marker to the last cacheable block in the request. container: Container identifier for reuse across requests. inference_geo: Specifies the geographic region for inference processing. If not specified, the workspace's `default_inference_geo` is used. metadata: An object describing metadata about the request. output_config: Configuration options for the model's output, such as the output format. service_tier: Determines whether to use priority capacity (if available) or standard capacity for this request. Anthropic offers different levels of service for your API requests. See [service-tiers](https://platform.claude.com/docs/en/api/service-tiers) for details. stop_sequences: Custom text sequences that will cause the model to stop generating. Our models will normally stop when they have naturally completed their turn, which will result in a response `stop_reason` of `"end_turn"`. If you want the model to stop generating when it encounters custom strings of text, you can use the `stop_sequences` parameter. If the model encounters one of the custom sequences, the response `stop_reason` value will be `"stop_sequence"` and the response `stop_sequence` value will contain the matched stop sequence. system: System prompt. A system prompt is a way of providing context and instructions to Claude, such as specifying a particular goal or role. See our [guide to system prompts](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role). temperature: Amount of randomness injected into the response. Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` for analytical / multiple choice, and closer to `1.0` for creative and generative tasks. Note that even with `temperature` of `0.0`, the results will not be fully deterministic. thinking: Configuration for enabling Claude's extended thinking. When enabled, responses include `thinking` content blocks showing Claude's thinking process before the final answer. Requires a minimum budget of 1,024 tokens and counts towards your `max_tokens` limit. See [extended thinking](https://platform.claude.com/docs/en/build-with-claude/extended-thinking) for details. tool_choice: How the model should use the provided tools. The model can use a specific tool, any available tool, decide by itself, or not use tools at all. tools: Definitions of tools that the model may use. If you include `tools` in your API request, the model may return `tool_use` content blocks that represent the model's use of those tools. You can then run those tools using the tool input generated by the model and then optionally return results back to the model using `tool_result` content blocks. There are two types of tools: **client tools** and **server tools**. The behavior described below applies to client tools. For [server tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools), see their individual documentation as each has its own behavior (e.g., the [web search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool)). Each tool definition includes: - `name`: Name of the tool. - `description`: Optional, but strongly-recommended description of the tool. - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the tool `input` shape that the model will produce in `tool_use` output content blocks. For example, if you defined `tools` as: ```json [ { "name": "get_stock_price", "description": "Get the current stock price for a given ticker symbol.", "input_schema": { "type": "object", "properties": { "ticker": { "type": "string", "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." } }, "required": ["ticker"] } } ] ``` And then asked the model "What's the S&P 500 at today?", the model might produce `tool_use` content blocks in the response like this: ```json [ { "type": "tool_use", "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "name": "get_stock_price", "input": { "ticker": "^GSPC" } } ] ``` You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an input, and return the following back to the model in a subsequent `user` message: ```json [ { "type": "tool_result", "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "content": "259.75 USD" } ] ``` Tools can be used for workflows that include running client-side tools and functions, or more generally whenever you want the model to produce a particular JSON structure of output. See our [guide](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview) for more details. top_k: Only sample from the top K options for each subsequent token. Used to remove "long tail" low probability responses. [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). Recommended for advanced use cases only. top_p: Use nucleus sampling. In nucleus sampling, we compute the cumulative distribution over all the options for each subsequent token in decreasing probability order and cut it off once it reaches a particular probability specified by `top_p`. Recommended for advanced use cases only. user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a party other than your organization. Requires the `user-profiles` beta header. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ ... @required_args(["max_tokens", "messages", "model"], ["max_tokens", "messages", "model", "stream"]) async def create( self, *, max_tokens: int, messages: Iterable[MessageParam], model: ModelParam, cache_control: Optional[CacheControlEphemeralParam] | Omit = omit, container: Optional[str] | Omit = omit, inference_geo: Optional[str] | Omit = omit, metadata: MetadataParam | Omit = omit, output_config: OutputConfigParam | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, stream: Literal[False] | Literal[True] | Omit = omit, system: Union[str, Iterable[TextBlockParam]] | Omit = omit, temperature: float | Omit = omit, thinking: ThinkingConfigParam | Omit = omit, tool_choice: ToolChoiceParam | Omit = omit, tools: Iterable[ToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> Message | AsyncStream[RawMessageStreamEvent]: if not stream and not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: timeout = self._client._calculate_nonstreaming_timeout( max_tokens, MODEL_NONSTREAMING_TOKENS.get(model, None) ) if model in DEPRECATED_MODELS: warnings.warn( f"The model '{model}' is deprecated and will reach end-of-life on {DEPRECATED_MODELS[model]}.\nPlease migrate to a newer model. Visit https://docs.anthropic.com/en/docs/resources/model-deprecations for more information.", DeprecationWarning, stacklevel=3, ) if model in MODELS_TO_WARN_WITH_THINKING_ENABLED and thinking and thinking["type"] == "enabled": warnings.warn( f"Using Claude with {model} and 'thinking.type=enabled' is deprecated. Use 'thinking.type=adaptive' instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking", UserWarning, stacklevel=3, ) extra_headers = {**strip_not_given({"anthropic-user-profile-id": user_profile_id}), **(extra_headers or {})} return await self._post( "/v1/messages", body=await async_maybe_transform( { "max_tokens": max_tokens, "messages": messages, "model": model, "cache_control": cache_control, "container": container, "inference_geo": inference_geo, "metadata": metadata, "output_config": output_config, "service_tier": service_tier, "stop_sequences": stop_sequences, "stream": stream, "system": system, "temperature": temperature, "thinking": thinking, "tool_choice": tool_choice, "tools": tools, "top_k": top_k, "top_p": top_p, }, message_create_params.MessageCreateParamsStreaming if stream else message_create_params.MessageCreateParamsNonStreaming, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=Message, stream=stream or False, stream_cls=AsyncStream[RawMessageStreamEvent], ) def stream( self, *, max_tokens: int, messages: Iterable[MessageParam], model: ModelParam, cache_control: Optional[CacheControlEphemeralParam] | Omit = omit, inference_geo: Optional[str] | Omit = omit, metadata: MetadataParam | Omit = omit, output_config: OutputConfigParam | Omit = omit, output_format: None | JSONOutputFormatParam | type[ResponseFormatT] | Omit = omit, container: Optional[str] | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, system: Union[str, Iterable[TextBlockParam]] | Omit = omit, temperature: float | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, thinking: ThinkingConfigParam | Omit = omit, tool_choice: ToolChoiceParam | Omit = omit, tools: Iterable[ToolUnionParam] | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> AsyncMessageStreamManager[ResponseFormatT]: """Create a Message stream""" if model in DEPRECATED_MODELS: warnings.warn( f"The model '{model}' is deprecated and will reach end-of-life on {DEPRECATED_MODELS[model]}.\nPlease migrate to a newer model. Visit https://docs.anthropic.com/en/docs/resources/model-deprecations for more information.", DeprecationWarning, stacklevel=3, ) if model in MODELS_TO_WARN_WITH_THINKING_ENABLED and thinking and thinking["type"] == "enabled": warnings.warn( f"Using Claude with {model} and 'thinking.type=enabled' is deprecated. Use 'thinking.type=adaptive' instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking", UserWarning, stacklevel=3, ) extra_headers = { **strip_not_given({"anthropic-user-profile-id": user_profile_id}), _STAINLESS_HELPER_METHOD_HEADER: _HELPER_METHOD_STREAM, _STAINLESS_STREAM_HELPER_HEADER: "messages", **(extra_headers or {}), } transformed_output_format: Optional[JSONOutputFormatParam] | NotGiven = NOT_GIVEN if is_dict(output_format): transformed_output_format = cast(JSONOutputFormatParam, output_format) elif is_given(output_format) and output_format is not None: adapted_type: TypeAdapter[ResponseFormatT] = TypeAdapter(output_format) try: schema = adapted_type.json_schema() transformed_output_format = JSONOutputFormatParam(schema=transform_schema(schema), type="json_schema") except pydantic.errors.PydanticSchemaGenerationError as e: raise TypeError( ( "Could not generate JSON schema for the given `output_format` type. " "Use a type that works with `pydantic.TypeAdapter`" ) ) from e # Merge output_format into output_config merged_output_config: OutputConfigParam | Omit = omit if is_given(transformed_output_format): if is_given(output_config): merged_output_config = {**output_config, "format": transformed_output_format} else: merged_output_config = {"format": transformed_output_format} elif is_given(output_config): merged_output_config = output_config request = self._post( "/v1/messages", body=maybe_transform( { "max_tokens": max_tokens, "messages": messages, "model": model, "cache_control": cache_control, "inference_geo": inference_geo, "metadata": metadata, "output_config": merged_output_config, "container": container, "service_tier": service_tier, "stop_sequences": stop_sequences, "system": system, "temperature": temperature, "top_k": top_k, "top_p": top_p, "tools": tools, "thinking": thinking, "tool_choice": tool_choice, "stream": True, }, message_create_params.MessageCreateParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=Message, stream=True, stream_cls=AsyncStream[RawMessageStreamEvent], ) return AsyncMessageStreamManager( request, output_format=NOT_GIVEN if is_dict(output_format) else cast(ResponseFormatT, output_format), ) async def parse( self, *, max_tokens: int, messages: Iterable[MessageParam], model: ModelParam, metadata: MetadataParam | Omit = omit, output_config: OutputConfigParam | Omit = omit, output_format: Optional[type[ResponseFormatT]] | Omit = omit, service_tier: Literal["auto", "standard_only"] | Omit = omit, stop_sequences: SequenceNotStr[str] | Omit = omit, stream: Literal[False] | Literal[True] | Omit = omit, system: Union[str, Iterable[TextBlockParam]] | Omit = omit, temperature: float | Omit = omit, thinking: ThinkingConfigParam | Omit = omit, tool_choice: ToolChoiceParam | Omit = omit, tools: Iterable[ToolUnionParam] | Omit = omit, top_k: int | Omit = omit, top_p: float | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, ) -> ParsedMessage[ResponseFormatT]: if not stream and not is_given(timeout) and self._client.timeout == DEFAULT_TIMEOUT: timeout = self._client._calculate_nonstreaming_timeout( max_tokens, MODEL_NONSTREAMING_TOKENS.get(model, None) ) if model in DEPRECATED_MODELS: warnings.warn( f"The model '{model}' is deprecated and will reach end-of-life on {DEPRECATED_MODELS[model]}.\nPlease migrate to a newer model. Visit https://docs.anthropic.com/en/docs/resources/model-deprecations for more information.", DeprecationWarning, stacklevel=3, ) if model in MODELS_TO_WARN_WITH_THINKING_ENABLED and thinking and thinking["type"] == "enabled": warnings.warn( f"Using Claude with {model} and 'thinking.type=enabled' is deprecated. Use 'thinking.type=adaptive' instead which results in better model performance in our testing: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking", UserWarning, stacklevel=3, ) extra_headers = merge_headers( _helper_header("messages.parse"), extra_headers or {}, ) transformed_output_format: Optional[JSONOutputFormatParam] | NotGiven = NOT_GIVEN if is_given(output_format) and output_format is not None: adapted_type: TypeAdapter[ResponseFormatT] = TypeAdapter(output_format) try: schema = adapted_type.json_schema() transformed_output_format = JSONOutputFormatParam(schema=transform_schema(schema), type="json_schema") except pydantic.errors.PydanticSchemaGenerationError as e: raise TypeError( ( "Could not generate JSON schema for the given `output_format` type. " "Use a type that works with `pydantic.TypeAdapter`" ) ) from e def parser(response: Message) -> ParsedMessage[ResponseFormatT]: return parse_response( response=response, output_format=cast( ResponseFormatT, output_format if is_given(output_format) and output_format is not None else NOT_GIVEN, ), ) # Merge output_format into output_config merged_output_config: OutputConfigParam | Omit = omit if is_given(transformed_output_format): if is_given(output_config): merged_output_config = {**output_config, "format": transformed_output_format} else: merged_output_config = {"format": transformed_output_format} elif is_given(output_config): merged_output_config = output_config return await self._post( "/v1/messages", body=await async_maybe_transform( { "max_tokens": max_tokens, "messages": messages, "model": model, "metadata": metadata, "output_config": merged_output_config, "service_tier": service_tier, "stop_sequences": stop_sequences, "stream": stream, "system": system, "temperature": temperature, "thinking": thinking, "tool_choice": tool_choice, "tools": tools, "top_k": top_k, "top_p": top_p, }, message_create_params.MessageCreateParamsNonStreaming, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, post_parser=parser, ), cast_to=cast(Type[ParsedMessage[ResponseFormatT]], Message), stream=False, ) async def count_tokens( self, *, messages: Iterable[MessageParam], model: ModelParam, cache_control: Optional[CacheControlEphemeralParam] | Omit = omit, output_config: OutputConfigParam | Omit = omit, output_format: None | JSONOutputFormatParam | type | Omit = omit, system: Union[str, Iterable[TextBlockParam]] | Omit = omit, thinking: ThinkingConfigParam | Omit = omit, tool_choice: ToolChoiceParam | Omit = omit, tools: Iterable[MessageCountTokensToolParam] | Omit = omit, user_profile_id: str | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> MessageTokensCount: """ Count the number of tokens in a Message. The Token Count API can be used to count the number of tokens in a Message, including tools, images, and documents, without creating it. Learn more about token counting in our [user guide](https://platform.claude.com/docs/en/build-with-claude/token-counting) Args: messages: Input messages. Our models are trained to operate on alternating `user` and `assistant` conversational turns. When creating a new `Message`, you specify the prior conversational turns with the `messages` parameter, and the model then generates the next `Message` in the conversation. Consecutive `user` or `assistant` turns in your request will be combined into a single turn. Each input message must be an object with a `role` and `content`. You can specify a single `user`-role message, or you can include multiple `user` and `assistant` messages. If the final message uses the `assistant` role, the response content will continue immediately from the content in that message. This can be used to constrain part of the model's response. Example with a single `user` message: ```json [{ "role": "user", "content": "Hello, Claude" }] ``` Example with multiple conversational turns: ```json [ { "role": "user", "content": "Hello there." }, { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, { "role": "user", "content": "Can you explain LLMs in plain English?" } ] ``` Example with a partially-filled response from Claude: ```json [ { "role": "user", "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" }, { "role": "assistant", "content": "The best answer is (" } ] ``` Each input message `content` may be either a single `string` or an array of content blocks, where each block has a specific `type`. Using a `string` for `content` is shorthand for an array of one content block of type `"text"`. The following input messages are equivalent: ```json { "role": "user", "content": "Hello, Claude" } ``` ```json { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } ``` See [input examples](https://platform.claude.com/docs/en/build-with-claude/working-with-messages). Note that if you want to include a [system prompt](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role), you can use the top-level `system` parameter — there is no `"system"` role for input messages in the Messages API. There is a limit of 100,000 messages in a single request. model: The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. cache_control: Top-level cache control automatically applies a cache_control marker to the last cacheable block in the request. output_config: Configuration options for the model's output, such as the output format. system: System prompt. A system prompt is a way of providing context and instructions to Claude, such as specifying a particular goal or role. See our [guide to system prompts](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role). thinking: Configuration for enabling Claude's extended thinking. When enabled, responses include `thinking` content blocks showing Claude's thinking process before the final answer. Requires a minimum budget of 1,024 tokens and counts towards your `max_tokens` limit. See [extended thinking](https://platform.claude.com/docs/en/build-with-claude/extended-thinking) for details. tool_choice: How the model should use the provided tools. The model can use a specific tool, any available tool, decide by itself, or not use tools at all. tools: Definitions of tools that the model may use. If you include `tools` in your API request, the model may return `tool_use` content blocks that represent the model's use of those tools. You can then run those tools using the tool input generated by the model and then optionally return results back to the model using `tool_result` content blocks. There are two types of tools: **client tools** and **server tools**. The behavior described below applies to client tools. For [server tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools), see their individual documentation as each has its own behavior (e.g., the [web search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool)). Each tool definition includes: - `name`: Name of the tool. - `description`: Optional, but strongly-recommended description of the tool. - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the tool `input` shape that the model will produce in `tool_use` output content blocks. For example, if you defined `tools` as: ```json [ { "name": "get_stock_price", "description": "Get the current stock price for a given ticker symbol.", "input_schema": { "type": "object", "properties": { "ticker": { "type": "string", "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." } }, "required": ["ticker"] } } ] ``` And then asked the model "What's the S&P 500 at today?", the model might produce `tool_use` content blocks in the response like this: ```json [ { "type": "tool_use", "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "name": "get_stock_price", "input": { "ticker": "^GSPC" } } ] ``` You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an input, and return the following back to the model in a subsequent `user` message: ```json [ { "type": "tool_result", "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "content": "259.75 USD" } ] ``` Tools can be used for workflows that include running client-side tools and functions, or more generally whenever you want the model to produce a particular JSON structure of output. See our [guide](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview) for more details. user_profile_id: The user profile ID to attribute this request to. Use when acting on behalf of a party other than your organization. Requires the `user-profiles` beta header. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = {**strip_not_given({"anthropic-user-profile-id": user_profile_id}), **(extra_headers or {})} # Transform output_format if provided transformed_output_format: Optional[JSONOutputFormatParam] | NotGiven = NOT_GIVEN if is_dict(output_format): transformed_output_format = cast(JSONOutputFormatParam, output_format) elif is_given(output_format) and output_format is not None: adapted_type: TypeAdapter[type] = TypeAdapter(output_format) try: schema = adapted_type.json_schema() transformed_output_format = JSONOutputFormatParam(schema=transform_schema(schema), type="json_schema") except pydantic.errors.PydanticSchemaGenerationError as e: raise TypeError( ( "Could not generate JSON schema for the given `output_format` type. " "Use a type that works with `pydantic.TypeAdapter`" ) ) from e # Merge output_format into output_config merged_output_config: OutputConfigParam | Omit = omit if is_given(transformed_output_format): if is_given(output_config): merged_output_config = {**output_config, "format": transformed_output_format} else: merged_output_config = {"format": transformed_output_format} elif is_given(output_config): merged_output_config = output_config return await self._post( "/v1/messages/count_tokens", body=await async_maybe_transform( { "messages": messages, "model": model, "cache_control": cache_control, "output_config": merged_output_config, "system": system, "thinking": thinking, "tool_choice": tool_choice, "tools": tools, }, message_count_tokens_params.MessageCountTokensParams, ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=MessageTokensCount, ) class MessagesWithRawResponse: def __init__(self, messages: Messages) -> None: self._messages = messages self.create = _legacy_response.to_raw_response_wrapper( messages.create, ) self.count_tokens = _legacy_response.to_raw_response_wrapper( messages.count_tokens, ) @cached_property def batches(self) -> BatchesWithRawResponse: return BatchesWithRawResponse(self._messages.batches) class AsyncMessagesWithRawResponse: def __init__(self, messages: AsyncMessages) -> None: self._messages = messages self.create = _legacy_response.async_to_raw_response_wrapper( messages.create, ) self.count_tokens = _legacy_response.async_to_raw_response_wrapper( messages.count_tokens, ) @cached_property def batches(self) -> AsyncBatchesWithRawResponse: return AsyncBatchesWithRawResponse(self._messages.batches) class MessagesWithStreamingResponse: def __init__(self, messages: Messages) -> None: self._messages = messages self.create = to_streamed_response_wrapper( messages.create, ) self.count_tokens = to_streamed_response_wrapper( messages.count_tokens, ) @cached_property def batches(self) -> BatchesWithStreamingResponse: return BatchesWithStreamingResponse(self._messages.batches) class AsyncMessagesWithStreamingResponse: def __init__(self, messages: AsyncMessages) -> None: self._messages = messages self.create = async_to_streamed_response_wrapper( messages.create, ) self.count_tokens = async_to_streamed_response_wrapper( messages.count_tokens, ) @cached_property def batches(self) -> AsyncBatchesWithStreamingResponse: return AsyncBatchesWithStreamingResponse(self._messages.batches) anthropic-sdk-python-0.120.2/src/anthropic/resources/models.py000066400000000000000000000303031523216435200243700ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List import httpx from .. import _legacy_response from ..types import model_list_params from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given from .._utils import is_given, path_template, maybe_transform, strip_not_given from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper from ..pagination import SyncPage, AsyncPage from .._base_client import AsyncPaginator, make_request_options from ..types.model_info import ModelInfo from ..types.anthropic_beta_param import AnthropicBetaParam __all__ = ["Models", "AsyncModels"] class Models(SyncAPIResource): @cached_property def with_raw_response(self) -> ModelsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return ModelsWithRawResponse(self) @cached_property def with_streaming_response(self) -> ModelsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return ModelsWithStreamingResponse(self) def retrieve( self, model_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ModelInfo: """ Get a specific model. The Models API response can be used to determine information about a specific model or resolve a model alias to a model ID. Args: model_id: Model identifier or alias. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not model_id: raise ValueError(f"Expected a non-empty value for `model_id` but received {model_id!r}") extra_headers = { **strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), **(extra_headers or {}), } return self._get( path_template("/v1/models/{model_id}", model_id=model_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=ModelInfo, ) def list( self, *, after_id: str | Omit = omit, before_id: str | Omit = omit, limit: int | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> SyncPage[ModelInfo]: """ List available models. The Models API response can be used to determine which models are available for use in the API. More recently released models are listed first. Args: after_id: ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately after this object. before_id: ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately before this object. limit: Number of items to return per page. Defaults to `20`. Ranges from `1` to `1000`. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), **(extra_headers or {}), } return self._get_api_list( "/v1/models", page=SyncPage[ModelInfo], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "after_id": after_id, "before_id": before_id, "limit": limit, }, model_list_params.ModelListParams, ), ), model=ModelInfo, ) class AsyncModels(AsyncAPIResource): @cached_property def with_raw_response(self) -> AsyncModelsWithRawResponse: """ This property can be used as a prefix for any HTTP method call to return the raw response object instead of the parsed content. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#accessing-raw-response-data-eg-headers """ return AsyncModelsWithRawResponse(self) @cached_property def with_streaming_response(self) -> AsyncModelsWithStreamingResponse: """ An alternative to `.with_raw_response` that doesn't eagerly read the response body. For more information, see https://www.github.com/anthropics/anthropic-sdk-python#with_streaming_response """ return AsyncModelsWithStreamingResponse(self) async def retrieve( self, model_id: str, *, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> ModelInfo: """ Get a specific model. The Models API response can be used to determine information about a specific model or resolve a model alias to a model ID. Args: model_id: Model identifier or alias. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ if not model_id: raise ValueError(f"Expected a non-empty value for `model_id` but received {model_id!r}") extra_headers = { **strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), **(extra_headers or {}), } return await self._get( path_template("/v1/models/{model_id}", model_id=model_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), cast_to=ModelInfo, ) def list( self, *, after_id: str | Omit = omit, before_id: str | Omit = omit, limit: int | Omit = omit, betas: List[AnthropicBetaParam] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, extra_query: Query | None = None, extra_body: Body | None = None, timeout: float | httpx.Timeout | None | NotGiven = not_given, ) -> AsyncPaginator[ModelInfo, AsyncPage[ModelInfo]]: """ List available models. The Models API response can be used to determine which models are available for use in the API. More recently released models are listed first. Args: after_id: ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately after this object. before_id: ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately before this object. limit: Number of items to return per page. Defaults to `20`. Ranges from `1` to `1000`. betas: Optional header to specify the beta version(s) you want to use. extra_headers: Send extra headers extra_query: Add additional query parameters to the request extra_body: Add additional JSON properties to the request timeout: Override the client-level default timeout for this request, in seconds """ extra_headers = { **strip_not_given({"anthropic-beta": ",".join(str(e) for e in betas) if is_given(betas) else not_given}), **(extra_headers or {}), } return self._get_api_list( "/v1/models", page=AsyncPage[ModelInfo], options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout, query=maybe_transform( { "after_id": after_id, "before_id": before_id, "limit": limit, }, model_list_params.ModelListParams, ), ), model=ModelInfo, ) class ModelsWithRawResponse: def __init__(self, models: Models) -> None: self._models = models self.retrieve = _legacy_response.to_raw_response_wrapper( models.retrieve, ) self.list = _legacy_response.to_raw_response_wrapper( models.list, ) class AsyncModelsWithRawResponse: def __init__(self, models: AsyncModels) -> None: self._models = models self.retrieve = _legacy_response.async_to_raw_response_wrapper( models.retrieve, ) self.list = _legacy_response.async_to_raw_response_wrapper( models.list, ) class ModelsWithStreamingResponse: def __init__(self, models: Models) -> None: self._models = models self.retrieve = to_streamed_response_wrapper( models.retrieve, ) self.list = to_streamed_response_wrapper( models.list, ) class AsyncModelsWithStreamingResponse: def __init__(self, models: AsyncModels) -> None: self._models = models self.retrieve = async_to_streamed_response_wrapper( models.retrieve, ) self.list = async_to_streamed_response_wrapper( models.list, ) anthropic-sdk-python-0.120.2/src/anthropic/tools/000077500000000000000000000000001523216435200216625ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/tools/__init__.py000066400000000000000000000000261523216435200237710ustar00rootroot00000000000000from .memory import * anthropic-sdk-python-0.120.2/src/anthropic/tools/memory.py000066400000000000000000000005351523216435200235470ustar00rootroot00000000000000from ..lib.tools._beta_builtin_memory_tool import ( BetaAbstractMemoryTool, BetaAsyncAbstractMemoryTool, BetaLocalFilesystemMemoryTool, BetaAsyncLocalFilesystemMemoryTool, ) __all__ = [ "BetaLocalFilesystemMemoryTool", "BetaAsyncLocalFilesystemMemoryTool", "BetaAbstractMemoryTool", "BetaAsyncAbstractMemoryTool", ] anthropic-sdk-python-0.120.2/src/anthropic/types/000077500000000000000000000000001523216435200216665ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/__init__.py000066400000000000000000000455331523216435200240110ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from .model import Model as Model from .usage import Usage as Usage from .shared import ( ErrorType as ErrorType, ErrorObject as ErrorObject, BillingError as BillingError, ErrorResponse as ErrorResponse, NotFoundError as NotFoundError, APIErrorObject as APIErrorObject, RateLimitError as RateLimitError, OverloadedError as OverloadedError, PermissionError as PermissionError, AuthenticationError as AuthenticationError, GatewayTimeoutError as GatewayTimeoutError, InvalidRequestError as InvalidRequestError, ) from .message import Message as Message from .container import Container as Container from .beta_error import BetaError as BetaError from .completion import Completion as Completion from .model_info import ModelInfo as ModelInfo from .text_block import TextBlock as TextBlock from .text_delta import TextDelta as TextDelta from .tool_param import ToolParam as ToolParam from .model_param import ModelParam as ModelParam from .stop_reason import StopReason as StopReason from .content_block import ContentBlock as ContentBlock from .direct_caller import DirectCaller as DirectCaller from .message_param import MessageParam as MessageParam from .text_citation import TextCitation as TextCitation from .beta_api_error import BetaAPIError as BetaAPIError from .cache_creation import CacheCreation as CacheCreation from .document_block import DocumentBlock as DocumentBlock from .metadata_param import MetadataParam as MetadataParam from .parsed_message import ( ParsedMessage as ParsedMessage, ParsedTextBlock as ParsedTextBlock, ParsedContentBlock as ParsedContentBlock, ) from .thinking_block import ThinkingBlock as ThinkingBlock from .thinking_delta import ThinkingDelta as ThinkingDelta from .thinking_types import ThinkingTypes as ThinkingTypes from .tool_use_block import ToolUseBlock as ToolUseBlock from .citations_delta import CitationsDelta as CitationsDelta from .signature_delta import SignatureDelta as SignatureDelta from .web_fetch_block import WebFetchBlock as WebFetchBlock from .citations_config import CitationsConfig as CitationsConfig from .input_json_delta import InputJSONDelta as InputJSONDelta from .text_block_param import TextBlockParam as TextBlockParam from .tool_union_param import ToolUnionParam as ToolUnionParam from .base64_pdf_source import Base64PDFSource as Base64PDFSource from .effort_capability import EffortCapability as EffortCapability from .image_block_param import ImageBlockParam as ImageBlockParam from .model_list_params import ModelListParams as ModelListParams from .plain_text_source import PlainTextSource as PlainTextSource from .server_tool_usage import ServerToolUsage as ServerToolUsage from .tool_choice_param import ToolChoiceParam as ToolChoiceParam from .beta_billing_error import BetaBillingError as BetaBillingError from .capability_support import CapabilitySupport as CapabilitySupport from .message_stop_event import MessageStopEvent as MessageStopEvent from .model_capabilities import ModelCapabilities as ModelCapabilities from .server_tool_caller import ServerToolCaller as ServerToolCaller from .beta_error_response import BetaErrorResponse as BetaErrorResponse from .content_block_param import ContentBlockParam as ContentBlockParam from .direct_caller_param import DirectCallerParam as DirectCallerParam from .message_delta_event import MessageDeltaEvent as MessageDeltaEvent from .message_delta_usage import MessageDeltaUsage as MessageDeltaUsage from .message_start_event import MessageStartEvent as MessageStartEvent from .output_config_param import OutputConfigParam as OutputConfigParam from .text_citation_param import TextCitationParam as TextCitationParam from .thinking_capability import ThinkingCapability as ThinkingCapability from .user_location_param import UserLocationParam as UserLocationParam from .anthropic_beta_param import AnthropicBetaParam as AnthropicBetaParam from .beta_not_found_error import BetaNotFoundError as BetaNotFoundError from .document_block_param import DocumentBlockParam as DocumentBlockParam from .message_stream_event import MessageStreamEvent as MessageStreamEvent from .message_tokens_count import MessageTokensCount as MessageTokensCount from .refusal_stop_details import RefusalStopDetails as RefusalStopDetails from .thinking_block_param import ThinkingBlockParam as ThinkingBlockParam from .tool_reference_block import ToolReferenceBlock as ToolReferenceBlock from .tool_use_block_param import ToolUseBlockParam as ToolUseBlockParam from .url_pdf_source_param import URLPDFSourceParam as URLPDFSourceParam from .beta_overloaded_error import BetaOverloadedError as BetaOverloadedError from .beta_permission_error import BetaPermissionError as BetaPermissionError from .beta_rate_limit_error import BetaRateLimitError as BetaRateLimitError from .message_create_params import MessageCreateParams as MessageCreateParams from .output_tokens_details import OutputTokensDetails as OutputTokensDetails from .server_tool_use_block import ServerToolUseBlock as ServerToolUseBlock from .thinking_config_param import ThinkingConfigParam as ThinkingConfigParam from .tool_choice_any_param import ToolChoiceAnyParam as ToolChoiceAnyParam from .web_fetch_block_param import WebFetchBlockParam as WebFetchBlockParam from .citation_char_location import CitationCharLocation as CitationCharLocation from .citation_page_location import CitationPageLocation as CitationPageLocation from .citations_config_param import CitationsConfigParam as CitationsConfigParam from .container_upload_block import ContainerUploadBlock as ContainerUploadBlock from .raw_message_stop_event import RawMessageStopEvent as RawMessageStopEvent from .tool_choice_auto_param import ToolChoiceAutoParam as ToolChoiceAutoParam from .tool_choice_none_param import ToolChoiceNoneParam as ToolChoiceNoneParam from .tool_choice_tool_param import ToolChoiceToolParam as ToolChoiceToolParam from .url_image_source_param import URLImageSourceParam as URLImageSourceParam from .base64_pdf_source_param import Base64PDFSourceParam as Base64PDFSourceParam from .plain_text_source_param import PlainTextSourceParam as PlainTextSourceParam from .raw_content_block_delta import RawContentBlockDelta as RawContentBlockDelta from .raw_message_delta_event import RawMessageDeltaEvent as RawMessageDeltaEvent from .raw_message_start_event import RawMessageStartEvent as RawMessageStartEvent from .redacted_thinking_block import RedactedThinkingBlock as RedactedThinkingBlock from .tool_result_block_param import ToolResultBlockParam as ToolResultBlockParam from .web_search_result_block import WebSearchResultBlock as WebSearchResultBlock from .completion_create_params import CompletionCreateParams as CompletionCreateParams from .content_block_stop_event import ContentBlockStopEvent as ContentBlockStopEvent from .json_output_format_param import JSONOutputFormatParam as JSONOutputFormatParam from .raw_message_stream_event import RawMessageStreamEvent as RawMessageStreamEvent from .server_tool_caller_param import ServerToolCallerParam as ServerToolCallerParam from .tool_bash_20250124_param import ToolBash20250124Param as ToolBash20250124Param from .base64_image_source_param import Base64ImageSourceParam as Base64ImageSourceParam from .beta_authentication_error import BetaAuthenticationError as BetaAuthenticationError from .content_block_delta_event import ContentBlockDeltaEvent as ContentBlockDeltaEvent from .content_block_start_event import ContentBlockStartEvent as ContentBlockStartEvent from .search_result_block_param import SearchResultBlockParam as SearchResultBlockParam from .beta_gateway_timeout_error import BetaGatewayTimeoutError as BetaGatewayTimeoutError from .beta_invalid_request_error import BetaInvalidRequestError as BetaInvalidRequestError from .content_block_source_param import ContentBlockSourceParam as ContentBlockSourceParam from .memory_tool_20250818_param import MemoryTool20250818Param as MemoryTool20250818Param from .tool_reference_block_param import ToolReferenceBlockParam as ToolReferenceBlockParam from .code_execution_output_block import CodeExecutionOutputBlock as CodeExecutionOutputBlock from .code_execution_result_block import CodeExecutionResultBlock as CodeExecutionResultBlock from .message_count_tokens_params import MessageCountTokensParams as MessageCountTokensParams from .server_tool_caller_20260120 import ServerToolCaller20260120 as ServerToolCaller20260120 from .server_tool_use_block_param import ServerToolUseBlockParam as ServerToolUseBlockParam from .web_fetch_tool_result_block import WebFetchToolResultBlock as WebFetchToolResultBlock from .citation_char_location_param import CitationCharLocationParam as CitationCharLocationParam from .citation_page_location_param import CitationPageLocationParam as CitationPageLocationParam from .container_upload_block_param import ContainerUploadBlockParam as ContainerUploadBlockParam from .raw_content_block_stop_event import RawContentBlockStopEvent as RawContentBlockStopEvent from .web_search_tool_result_block import WebSearchToolResultBlock as WebSearchToolResultBlock from .web_search_tool_result_error import WebSearchToolResultError as WebSearchToolResultError from .cache_control_ephemeral_param import CacheControlEphemeralParam as CacheControlEphemeralParam from .context_management_capability import ContextManagementCapability as ContextManagementCapability from .raw_content_block_delta_event import RawContentBlockDeltaEvent as RawContentBlockDeltaEvent from .raw_content_block_start_event import RawContentBlockStartEvent as RawContentBlockStartEvent from .redacted_thinking_block_param import RedactedThinkingBlockParam as RedactedThinkingBlockParam from .thinking_config_enabled_param import ThinkingConfigEnabledParam as ThinkingConfigEnabledParam from .tool_search_tool_result_block import ToolSearchToolResultBlock as ToolSearchToolResultBlock from .tool_search_tool_result_error import ToolSearchToolResultError as ToolSearchToolResultError from .web_fetch_tool_20250910_param import WebFetchTool20250910Param as WebFetchTool20250910Param from .web_fetch_tool_20260209_param import WebFetchTool20260209Param as WebFetchTool20260209Param from .web_fetch_tool_20260309_param import WebFetchTool20260309Param as WebFetchTool20260309Param from .web_fetch_tool_20260318_param import WebFetchTool20260318Param as WebFetchTool20260318Param from .web_search_result_block_param import WebSearchResultBlockParam as WebSearchResultBlockParam from .thinking_config_adaptive_param import ThinkingConfigAdaptiveParam as ThinkingConfigAdaptiveParam from .thinking_config_disabled_param import ThinkingConfigDisabledParam as ThinkingConfigDisabledParam from .web_search_tool_20250305_param import WebSearchTool20250305Param as WebSearchTool20250305Param from .web_search_tool_20260209_param import WebSearchTool20260209Param as WebSearchTool20260209Param from .web_search_tool_20260318_param import WebSearchTool20260318Param as WebSearchTool20260318Param from .citation_content_block_location import CitationContentBlockLocation as CitationContentBlockLocation from .message_count_tokens_tool_param import MessageCountTokensToolParam as MessageCountTokensToolParam from .tool_text_editor_20250124_param import ToolTextEditor20250124Param as ToolTextEditor20250124Param from .tool_text_editor_20250429_param import ToolTextEditor20250429Param as ToolTextEditor20250429Param from .tool_text_editor_20250728_param import ToolTextEditor20250728Param as ToolTextEditor20250728Param from .bash_code_execution_output_block import BashCodeExecutionOutputBlock as BashCodeExecutionOutputBlock from .bash_code_execution_result_block import BashCodeExecutionResultBlock as BashCodeExecutionResultBlock from .citations_search_result_location import CitationsSearchResultLocation as CitationsSearchResultLocation from .code_execution_tool_result_block import CodeExecutionToolResultBlock as CodeExecutionToolResultBlock from .code_execution_tool_result_error import CodeExecutionToolResultError as CodeExecutionToolResultError from .web_fetch_tool_result_error_code import WebFetchToolResultErrorCode as WebFetchToolResultErrorCode from .code_execution_output_block_param import CodeExecutionOutputBlockParam as CodeExecutionOutputBlockParam from .code_execution_result_block_param import CodeExecutionResultBlockParam as CodeExecutionResultBlockParam from .server_tool_caller_20260120_param import ServerToolCaller20260120Param as ServerToolCaller20260120Param from .web_fetch_tool_result_block_param import WebFetchToolResultBlockParam as WebFetchToolResultBlockParam from .web_fetch_tool_result_error_block import WebFetchToolResultErrorBlock as WebFetchToolResultErrorBlock from .web_search_tool_result_error_code import WebSearchToolResultErrorCode as WebSearchToolResultErrorCode from .code_execution_tool_20250522_param import CodeExecutionTool20250522Param as CodeExecutionTool20250522Param from .code_execution_tool_20250825_param import CodeExecutionTool20250825Param as CodeExecutionTool20250825Param from .code_execution_tool_20260120_param import CodeExecutionTool20260120Param as CodeExecutionTool20260120Param from .code_execution_tool_20260521_param import CodeExecutionTool20260521Param as CodeExecutionTool20260521Param from .content_block_source_content_param import ContentBlockSourceContentParam as ContentBlockSourceContentParam from .tool_search_tool_result_error_code import ToolSearchToolResultErrorCode as ToolSearchToolResultErrorCode from .web_search_tool_result_block_param import WebSearchToolResultBlockParam as WebSearchToolResultBlockParam from .mid_conversation_system_block_param import MidConversationSystemBlockParam as MidConversationSystemBlockParam from .tool_search_tool_result_block_param import ToolSearchToolResultBlockParam as ToolSearchToolResultBlockParam from .tool_search_tool_result_error_param import ToolSearchToolResultErrorParam as ToolSearchToolResultErrorParam from .web_search_tool_request_error_param import WebSearchToolRequestErrorParam as WebSearchToolRequestErrorParam from .citations_web_search_result_location import CitationsWebSearchResultLocation as CitationsWebSearchResultLocation from .tool_search_tool_bm25_20251119_param import ToolSearchToolBm25_20251119Param as ToolSearchToolBm25_20251119Param from .tool_search_tool_search_result_block import ToolSearchToolSearchResultBlock as ToolSearchToolSearchResultBlock from .web_search_tool_result_block_content import WebSearchToolResultBlockContent as WebSearchToolResultBlockContent from .bash_code_execution_tool_result_block import BashCodeExecutionToolResultBlock as BashCodeExecutionToolResultBlock from .bash_code_execution_tool_result_error import BashCodeExecutionToolResultError as BashCodeExecutionToolResultError from .citation_content_block_location_param import ( CitationContentBlockLocationParam as CitationContentBlockLocationParam, ) from .citation_search_result_location_param import ( CitationSearchResultLocationParam as CitationSearchResultLocationParam, ) from .code_execution_tool_result_error_code import CodeExecutionToolResultErrorCode as CodeExecutionToolResultErrorCode from .encrypted_code_execution_result_block import ( EncryptedCodeExecutionResultBlock as EncryptedCodeExecutionResultBlock, ) from .tool_search_tool_regex_20251119_param import ToolSearchToolRegex20251119Param as ToolSearchToolRegex20251119Param from .bash_code_execution_output_block_param import ( BashCodeExecutionOutputBlockParam as BashCodeExecutionOutputBlockParam, ) from .bash_code_execution_result_block_param import ( BashCodeExecutionResultBlockParam as BashCodeExecutionResultBlockParam, ) from .code_execution_tool_result_block_param import ( CodeExecutionToolResultBlockParam as CodeExecutionToolResultBlockParam, ) from .code_execution_tool_result_error_param import ( CodeExecutionToolResultErrorParam as CodeExecutionToolResultErrorParam, ) from .web_fetch_tool_result_error_block_param import ( WebFetchToolResultErrorBlockParam as WebFetchToolResultErrorBlockParam, ) from .code_execution_tool_result_block_content import ( CodeExecutionToolResultBlockContent as CodeExecutionToolResultBlockContent, ) from .citation_web_search_result_location_param import ( CitationWebSearchResultLocationParam as CitationWebSearchResultLocationParam, ) from .bash_code_execution_tool_result_error_code import ( BashCodeExecutionToolResultErrorCode as BashCodeExecutionToolResultErrorCode, ) from .tool_search_tool_search_result_block_param import ( ToolSearchToolSearchResultBlockParam as ToolSearchToolSearchResultBlockParam, ) from .bash_code_execution_tool_result_block_param import ( BashCodeExecutionToolResultBlockParam as BashCodeExecutionToolResultBlockParam, ) from .bash_code_execution_tool_result_error_param import ( BashCodeExecutionToolResultErrorParam as BashCodeExecutionToolResultErrorParam, ) from .encrypted_code_execution_result_block_param import ( EncryptedCodeExecutionResultBlockParam as EncryptedCodeExecutionResultBlockParam, ) from .text_editor_code_execution_tool_result_block import ( TextEditorCodeExecutionToolResultBlock as TextEditorCodeExecutionToolResultBlock, ) from .text_editor_code_execution_tool_result_error import ( TextEditorCodeExecutionToolResultError as TextEditorCodeExecutionToolResultError, ) from .text_editor_code_execution_view_result_block import ( TextEditorCodeExecutionViewResultBlock as TextEditorCodeExecutionViewResultBlock, ) from .text_editor_code_execution_create_result_block import ( TextEditorCodeExecutionCreateResultBlock as TextEditorCodeExecutionCreateResultBlock, ) from .web_search_tool_result_block_param_content_param import ( WebSearchToolResultBlockParamContentParam as WebSearchToolResultBlockParamContentParam, ) from .text_editor_code_execution_tool_result_error_code import ( TextEditorCodeExecutionToolResultErrorCode as TextEditorCodeExecutionToolResultErrorCode, ) from .text_editor_code_execution_tool_result_block_param import ( TextEditorCodeExecutionToolResultBlockParam as TextEditorCodeExecutionToolResultBlockParam, ) from .text_editor_code_execution_tool_result_error_param import ( TextEditorCodeExecutionToolResultErrorParam as TextEditorCodeExecutionToolResultErrorParam, ) from .text_editor_code_execution_view_result_block_param import ( TextEditorCodeExecutionViewResultBlockParam as TextEditorCodeExecutionViewResultBlockParam, ) from .text_editor_code_execution_str_replace_result_block import ( TextEditorCodeExecutionStrReplaceResultBlock as TextEditorCodeExecutionStrReplaceResultBlock, ) from .code_execution_tool_result_block_param_content_param import ( CodeExecutionToolResultBlockParamContentParam as CodeExecutionToolResultBlockParamContentParam, ) from .text_editor_code_execution_create_result_block_param import ( TextEditorCodeExecutionCreateResultBlockParam as TextEditorCodeExecutionCreateResultBlockParam, ) from .text_editor_code_execution_str_replace_result_block_param import ( TextEditorCodeExecutionStrReplaceResultBlockParam as TextEditorCodeExecutionStrReplaceResultBlockParam, ) anthropic-sdk-python-0.120.2/src/anthropic/types/anthropic_beta_param.py000066400000000000000000000027161523216435200264100ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from typing_extensions import Literal, TypeAlias __all__ = ["AnthropicBetaParam"] AnthropicBetaParam: TypeAlias = Union[ str, Literal[ "message-batches-2024-09-24", "prompt-caching-2024-07-31", "computer-use-2024-10-22", "computer-use-2025-01-24", "pdfs-2024-09-25", "token-counting-2024-11-01", "token-efficient-tools-2025-02-19", "output-128k-2025-02-19", "files-api-2025-04-14", "mcp-client-2025-04-04", "mcp-client-2025-11-20", "dev-full-thinking-2025-05-14", "interleaved-thinking-2025-05-14", "code-execution-2025-05-22", "extended-cache-ttl-2025-04-11", "context-1m-2025-08-07", "context-management-2025-06-27", "model-context-window-exceeded-2025-08-26", "skills-2025-10-02", "fast-mode-2026-02-01", "output-300k-2026-03-24", "user-profiles-2026-03-24", "advisor-tool-2026-03-01", "managed-agents-2026-04-01", "cache-diagnosis-2026-04-07", "dreaming-2026-04-21", "thinking-token-count-2026-05-13", "server-side-fallback-2026-06-01", "server-side-fallback-2026-07-01", "fallback-credit-2026-06-01", "fallback-credit-2026-07-01", "agent-memory-2026-07-22", ], ] anthropic-sdk-python-0.120.2/src/anthropic/types/base64_image_source_param.py000066400000000000000000000013251523216435200272270ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from typing_extensions import Literal, Required, Annotated, TypedDict from .._types import Base64FileInput from .._utils import PropertyInfo from .._models import set_pydantic_config __all__ = ["Base64ImageSourceParam"] class Base64ImageSourceParam(TypedDict, total=False): data: Required[Annotated[Union[str, Base64FileInput], PropertyInfo(format="base64")]] media_type: Required[Literal["image/jpeg", "image/png", "image/gif", "image/webp"]] type: Required[Literal["base64"]] set_pydantic_config(Base64ImageSourceParam, {"arbitrary_types_allowed": True}) anthropic-sdk-python-0.120.2/src/anthropic/types/base64_pdf_source.py000066400000000000000000000004701523216435200255360ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel __all__ = ["Base64PDFSource"] class Base64PDFSource(BaseModel): data: str media_type: Literal["application/pdf"] type: Literal["base64"] anthropic-sdk-python-0.120.2/src/anthropic/types/base64_pdf_source_param.py000066400000000000000000000012541523216435200267170ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from typing_extensions import Literal, Required, Annotated, TypedDict from .._types import Base64FileInput from .._utils import PropertyInfo from .._models import set_pydantic_config __all__ = ["Base64PDFSourceParam"] class Base64PDFSourceParam(TypedDict, total=False): data: Required[Annotated[Union[str, Base64FileInput], PropertyInfo(format="base64")]] media_type: Required[Literal["application/pdf"]] type: Required[Literal["base64"]] set_pydantic_config(Base64PDFSourceParam, {"arbitrary_types_allowed": True}) anthropic-sdk-python-0.120.2/src/anthropic/types/bash_code_execution_output_block.py000066400000000000000000000004751523216435200310320ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel __all__ = ["BashCodeExecutionOutputBlock"] class BashCodeExecutionOutputBlock(BaseModel): file_id: str type: Literal["bash_code_execution_output"] anthropic-sdk-python-0.120.2/src/anthropic/types/bash_code_execution_output_block_param.py000066400000000000000000000006001523216435200322000ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BashCodeExecutionOutputBlockParam"] class BashCodeExecutionOutputBlockParam(TypedDict, total=False): file_id: Required[str] type: Required[Literal["bash_code_execution_output"]] anthropic-sdk-python-0.120.2/src/anthropic/types/bash_code_execution_result_block.py000066400000000000000000000007671523216435200310140ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List from typing_extensions import Literal from .._models import BaseModel from .bash_code_execution_output_block import BashCodeExecutionOutputBlock __all__ = ["BashCodeExecutionResultBlock"] class BashCodeExecutionResultBlock(BaseModel): content: List[BashCodeExecutionOutputBlock] return_code: int stderr: str stdout: str type: Literal["bash_code_execution_result"] anthropic-sdk-python-0.120.2/src/anthropic/types/bash_code_execution_result_block_param.py000066400000000000000000000011611523216435200321610ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Iterable from typing_extensions import Literal, Required, TypedDict from .bash_code_execution_output_block_param import BashCodeExecutionOutputBlockParam __all__ = ["BashCodeExecutionResultBlockParam"] class BashCodeExecutionResultBlockParam(TypedDict, total=False): content: Required[Iterable[BashCodeExecutionOutputBlockParam]] return_code: Required[int] stderr: Required[str] stdout: Required[str] type: Required[Literal["bash_code_execution_result"]] anthropic-sdk-python-0.120.2/src/anthropic/types/bash_code_execution_tool_result_block.py000066400000000000000000000012161523216435200320370ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Literal, TypeAlias from .._models import BaseModel from .bash_code_execution_result_block import BashCodeExecutionResultBlock from .bash_code_execution_tool_result_error import BashCodeExecutionToolResultError __all__ = ["BashCodeExecutionToolResultBlock", "Content"] Content: TypeAlias = Union[BashCodeExecutionToolResultError, BashCodeExecutionResultBlock] class BashCodeExecutionToolResultBlock(BaseModel): content: Content tool_use_id: str type: Literal["bash_code_execution_tool_result"] anthropic-sdk-python-0.120.2/src/anthropic/types/bash_code_execution_tool_result_block_param.py000066400000000000000000000017101523216435200332160ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .cache_control_ephemeral_param import CacheControlEphemeralParam from .bash_code_execution_result_block_param import BashCodeExecutionResultBlockParam from .bash_code_execution_tool_result_error_param import BashCodeExecutionToolResultErrorParam __all__ = ["BashCodeExecutionToolResultBlockParam", "Content"] Content: TypeAlias = Union[BashCodeExecutionToolResultErrorParam, BashCodeExecutionResultBlockParam] class BashCodeExecutionToolResultBlockParam(TypedDict, total=False): content: Required[Content] tool_use_id: Required[str] type: Required[Literal["bash_code_execution_tool_result"]] cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" anthropic-sdk-python-0.120.2/src/anthropic/types/bash_code_execution_tool_result_error.py000066400000000000000000000007211523216435200320760ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel from .bash_code_execution_tool_result_error_code import BashCodeExecutionToolResultErrorCode __all__ = ["BashCodeExecutionToolResultError"] class BashCodeExecutionToolResultError(BaseModel): error_code: BashCodeExecutionToolResultErrorCode type: Literal["bash_code_execution_tool_result_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/bash_code_execution_tool_result_error_code.py000066400000000000000000000005531523216435200330730ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal, TypeAlias __all__ = ["BashCodeExecutionToolResultErrorCode"] BashCodeExecutionToolResultErrorCode: TypeAlias = Literal[ "invalid_tool_input", "unavailable", "too_many_requests", "execution_time_exceeded", "output_file_too_large" ] anthropic-sdk-python-0.120.2/src/anthropic/types/bash_code_execution_tool_result_error_param.py000066400000000000000000000010251523216435200332540ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict from .bash_code_execution_tool_result_error_code import BashCodeExecutionToolResultErrorCode __all__ = ["BashCodeExecutionToolResultErrorParam"] class BashCodeExecutionToolResultErrorParam(TypedDict, total=False): error_code: Required[BashCodeExecutionToolResultErrorCode] type: Required[Literal["bash_code_execution_tool_result_error"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/000077500000000000000000000000001523216435200226015ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/__init__.py000066400000000000000000001653021523216435200247210ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from .beta_dream import BetaDream as BetaDream from .beta_skill import BetaSkill as BetaSkill from .beta_usage import BetaUsage as BetaUsage from .beta_tunnel import BetaTunnel as BetaTunnel from .beta_message import BetaMessage as BetaMessage from .deleted_file import DeletedFile as DeletedFile from .beta_packages import BetaPackages as BetaPackages from .file_metadata import FileMetadata as FileMetadata from .beta_container import BetaContainer as BetaContainer from .beta_file_scope import BetaFileScope as BetaFileScope from .beta_model_info import BetaModelInfo as BetaModelInfo from .beta_text_block import BetaTextBlock as BetaTextBlock from .beta_text_delta import BetaTextDelta as BetaTextDelta from .beta_tool_param import BetaToolParam as BetaToolParam from .beta_diagnostics import BetaDiagnostics as BetaDiagnostics from .beta_dream_error import BetaDreamError as BetaDreamError from .beta_dream_input import BetaDreamInput as BetaDreamInput from .beta_dream_usage import BetaDreamUsage as BetaDreamUsage from .beta_environment import BetaEnvironment as BetaEnvironment from .beta_stop_reason import BetaStopReason as BetaStopReason from .file_list_params import FileListParams as FileListParams from .agent_list_params import AgentListParams as AgentListParams from .beta_cloud_config import BetaCloudConfig as BetaCloudConfig from .beta_dream_output import BetaDreamOutput as BetaDreamOutput from .beta_dream_status import BetaDreamStatus as BetaDreamStatus from .beta_skill_params import BetaSkillParams as BetaSkillParams from .beta_tunnel_token import BetaTunnelToken as BetaTunnelToken from .beta_user_profile import BetaUserProfile as BetaUserProfile from .dream_list_params import DreamListParams as DreamListParams from .model_list_params import ModelListParams as ModelListParams from .skill_list_params import SkillListParams as SkillListParams from .vault_list_params import VaultListParams as VaultListParams from .beta_content_block import BetaContentBlock as BetaContentBlock from .beta_direct_caller import BetaDirectCaller as BetaDirectCaller from .beta_fallback_info import BetaFallbackInfo as BetaFallbackInfo from .beta_message_param import BetaMessageParam as BetaMessageParam from .beta_text_citation import BetaTextCitation as BetaTextCitation from .file_upload_params import FileUploadParams as FileUploadParams from .tunnel_list_params import TunnelListParams as TunnelListParams from .agent_create_params import AgentCreateParams as AgentCreateParams from .agent_update_params import AgentUpdateParams as AgentUpdateParams from .beta_cache_creation import BetaCacheCreation as BetaCacheCreation from .beta_document_block import BetaDocumentBlock as BetaDocumentBlock from .beta_fallback_block import BetaFallbackBlock as BetaFallbackBlock from .beta_fallback_param import BetaFallbackParam as BetaFallbackParam from .beta_metadata_param import BetaMetadataParam as BetaMetadataParam from .beta_thinking_block import BetaThinkingBlock as BetaThinkingBlock from .beta_thinking_delta import BetaThinkingDelta as BetaThinkingDelta from .beta_thinking_types import BetaThinkingTypes as BetaThinkingTypes from .beta_tool_use_block import BetaToolUseBlock as BetaToolUseBlock from .dream_create_params import DreamCreateParams as DreamCreateParams from .session_list_params import SessionListParams as SessionListParams from .skill_create_params import SkillCreateParams as SkillCreateParams from .skill_list_response import SkillListResponse as SkillListResponse from .vault_create_params import VaultCreateParams as VaultCreateParams from .vault_update_params import VaultUpdateParams as VaultUpdateParams from .beta_citation_config import BetaCitationConfig as BetaCitationConfig from .beta_citations_delta import BetaCitationsDelta as BetaCitationsDelta from .beta_fallbacks_param import BetaFallbacksParam as BetaFallbacksParam from .beta_limited_network import BetaLimitedNetwork as BetaLimitedNetwork from .beta_packages_params import BetaPackagesParams as BetaPackagesParams from .beta_signature_delta import BetaSignatureDelta as BetaSignatureDelta from .beta_web_fetch_block import BetaWebFetchBlock as BetaWebFetchBlock from .tunnel_create_params import TunnelCreateParams as TunnelCreateParams from .unwrap_webhook_event import UnwrapWebhookEvent as UnwrapWebhookEvent from .agent_retrieve_params import AgentRetrieveParams as AgentRetrieveParams from .beta_compaction_block import BetaCompactionBlock as BetaCompactionBlock from .beta_container_params import BetaContainerParams as BetaContainerParams from .beta_input_json_delta import BetaInputJSONDelta as BetaInputJSONDelta from .beta_iterations_usage import BetaIterationsUsage as BetaIterationsUsage from .beta_text_block_param import BetaTextBlockParam as BetaTextBlockParam from .beta_tool_union_param import BetaToolUnionParam as BetaToolUnionParam from .message_create_params import MessageCreateParams as MessageCreateParams from .session_create_params import SessionCreateParams as SessionCreateParams from .session_update_params import SessionUpdateParams as SessionUpdateParams from .skill_create_response import SkillCreateResponse as SkillCreateResponse from .skill_delete_response import SkillDeleteResponse as SkillDeleteResponse from .beta_base64_pdf_source import BetaBase64PDFSource as BetaBase64PDFSource from .beta_diagnostics_param import BetaDiagnosticsParam as BetaDiagnosticsParam from .beta_dream_input_param import BetaDreamInputParam as BetaDreamInputParam from .beta_effort_capability import BetaEffortCapability as BetaEffortCapability from .beta_image_block_param import BetaImageBlockParam as BetaImageBlockParam from .beta_mcp_toolset_param import BetaMCPToolsetParam as BetaMCPToolsetParam from .beta_plain_text_source import BetaPlainTextSource as BetaPlainTextSource from .beta_server_tool_usage import BetaServerToolUsage as BetaServerToolUsage from .beta_tool_choice_param import BetaToolChoiceParam as BetaToolChoiceParam from .deployment_list_params import DeploymentListParams as DeploymentListParams from .beta_capability_support import BetaCapabilitySupport as BetaCapabilitySupport from .beta_dream_model_config import BetaDreamModelConfig as BetaDreamModelConfig from .beta_mcp_tool_use_block import BetaMCPToolUseBlock as BetaMCPToolUseBlock from .beta_model_capabilities import BetaModelCapabilities as BetaModelCapabilities from .beta_self_hosted_config import BetaSelfHostedConfig as BetaSelfHostedConfig from .beta_server_tool_caller import BetaServerToolCaller as BetaServerToolCaller from .beta_webhook_event_data import BetaWebhookEventData as BetaWebhookEventData from .environment_list_params import EnvironmentListParams as EnvironmentListParams from .skill_retrieve_response import SkillRetrieveResponse as SkillRetrieveResponse from .beta_cloud_config_params import BetaCloudConfigParams as BetaCloudConfigParams from .beta_content_block_param import BetaContentBlockParam as BetaContentBlockParam from .beta_direct_caller_param import BetaDirectCallerParam as BetaDirectCallerParam from .beta_fallback_info_param import BetaFallbackInfoParam as BetaFallbackInfoParam from .beta_message_delta_usage import BetaMessageDeltaUsage as BetaMessageDeltaUsage from .beta_output_config_param import BetaOutputConfigParam as BetaOutputConfigParam from .beta_text_citation_param import BetaTextCitationParam as BetaTextCitationParam from .beta_thinking_capability import BetaThinkingCapability as BetaThinkingCapability from .beta_user_location_param import BetaUserLocationParam as BetaUserLocationParam from .deployment_create_params import DeploymentCreateParams as DeploymentCreateParams from .deployment_update_params import DeploymentUpdateParams as DeploymentUpdateParams from .memory_store_list_params import MemoryStoreListParams as MemoryStoreListParams from .user_profile_list_params import UserProfileListParams as UserProfileListParams from .beta_advisor_result_block import BetaAdvisorResultBlock as BetaAdvisorResultBlock from .beta_dream_sessions_input import BetaDreamSessionsInput as BetaDreamSessionsInput from .beta_fallback_block_param import BetaFallbackBlockParam as BetaFallbackBlockParam from .beta_managed_agents_agent import BetaManagedAgentsAgent as BetaManagedAgentsAgent from .beta_managed_agents_model import BetaManagedAgentsModel as BetaManagedAgentsModel from .beta_managed_agents_vault import BetaManagedAgentsVault as BetaManagedAgentsVault from .beta_message_tokens_count import BetaMessageTokensCount as BetaMessageTokensCount from .beta_refusal_stop_details import BetaRefusalStopDetails as BetaRefusalStopDetails from .beta_thinking_block_param import BetaThinkingBlockParam as BetaThinkingBlockParam from .beta_thinking_turns_param import BetaThinkingTurnsParam as BetaThinkingTurnsParam from .beta_tool_reference_block import BetaToolReferenceBlock as BetaToolReferenceBlock from .beta_tool_use_block_param import BetaToolUseBlockParam as BetaToolUseBlockParam from .beta_tool_uses_keep_param import BetaToolUsesKeepParam as BetaToolUsesKeepParam from .beta_unrestricted_network import BetaUnrestrictedNetwork as BetaUnrestrictedNetwork from .beta_url_pdf_source_param import BetaURLPDFSourceParam as BetaURLPDFSourceParam from .environment_create_params import EnvironmentCreateParams as EnvironmentCreateParams from .environment_update_params import EnvironmentUpdateParams as EnvironmentUpdateParams from .beta_fallback_credit_usage import BetaFallbackCreditUsage as BetaFallbackCreditUsage from .beta_mcp_tool_config_param import BetaMCPToolConfigParam as BetaMCPToolConfigParam from .beta_mcp_tool_result_block import BetaMCPToolResultBlock as BetaMCPToolResultBlock from .beta_output_tokens_details import BetaOutputTokensDetails as BetaOutputTokensDetails from .beta_server_tool_use_block import BetaServerToolUseBlock as BetaServerToolUseBlock from .beta_thinking_config_param import BetaThinkingConfigParam as BetaThinkingConfigParam from .beta_tool_choice_any_param import BetaToolChoiceAnyParam as BetaToolChoiceAnyParam from .beta_web_fetch_block_param import BetaWebFetchBlockParam as BetaWebFetchBlockParam from .deployment_run_list_params import DeploymentRunListParams as DeploymentRunListParams from .memory_store_create_params import MemoryStoreCreateParams as MemoryStoreCreateParams from .memory_store_update_params import MemoryStoreUpdateParams as MemoryStoreUpdateParams from .tunnel_rotate_token_params import TunnelRotateTokenParams as TunnelRotateTokenParams from .user_profile_create_params import UserProfileCreateParams as UserProfileCreateParams from .user_profile_update_params import UserProfileUpdateParams as UserProfileUpdateParams from .beta_base64_pdf_block_param import BetaBase64PDFBlockParam as BetaBase64PDFBlockParam from .beta_cache_miss_unavailable import BetaCacheMissUnavailable as BetaCacheMissUnavailable from .beta_citation_char_location import BetaCitationCharLocation as BetaCitationCharLocation from .beta_citation_page_location import BetaCitationPageLocation as BetaCitationPageLocation from .beta_citations_config_param import BetaCitationsConfigParam as BetaCitationsConfigParam from .beta_compaction_block_param import BetaCompactionBlockParam as BetaCompactionBlockParam from .beta_container_upload_block import BetaContainerUploadBlock as BetaContainerUploadBlock from .beta_limited_network_params import BetaLimitedNetworkParams as BetaLimitedNetworkParams from .beta_managed_agents_session import BetaManagedAgentsSession as BetaManagedAgentsSession from .beta_raw_message_stop_event import BetaRawMessageStopEvent as BetaRawMessageStopEvent from .beta_tool_choice_auto_param import BetaToolChoiceAutoParam as BetaToolChoiceAutoParam from .beta_tool_choice_none_param import BetaToolChoiceNoneParam as BetaToolChoiceNoneParam from .beta_tool_choice_tool_param import BetaToolChoiceToolParam as BetaToolChoiceToolParam from .beta_url_image_source_param import BetaURLImageSourceParam as BetaURLImageSourceParam from .message_count_tokens_params import MessageCountTokensParams as MessageCountTokensParams from .beta_base64_pdf_source_param import BetaBase64PDFSourceParam as BetaBase64PDFSourceParam from .beta_file_image_source_param import BetaFileImageSourceParam as BetaFileImageSourceParam from .beta_managed_agents_schedule import BetaManagedAgentsSchedule as BetaManagedAgentsSchedule from .beta_message_iteration_usage import BetaMessageIterationUsage as BetaMessageIterationUsage from .beta_plain_text_source_param import BetaPlainTextSourceParam as BetaPlainTextSourceParam from .beta_raw_content_block_delta import BetaRawContentBlockDelta as BetaRawContentBlockDelta from .beta_raw_message_delta_event import BetaRawMessageDeltaEvent as BetaRawMessageDeltaEvent from .beta_raw_message_start_event import BetaRawMessageStartEvent as BetaRawMessageStartEvent from .beta_redacted_thinking_block import BetaRedactedThinkingBlock as BetaRedactedThinkingBlock from .beta_token_task_budget_param import BetaTokenTaskBudgetParam as BetaTokenTaskBudgetParam from .beta_tool_result_block_param import BetaToolResultBlockParam as BetaToolResultBlockParam from .beta_tool_uses_trigger_param import BetaToolUsesTriggerParam as BetaToolUsesTriggerParam from .beta_web_search_result_block import BetaWebSearchResultBlock as BetaWebSearchResultBlock from .beta_all_thinking_turns_param import BetaAllThinkingTurnsParam as BetaAllThinkingTurnsParam from .beta_cache_miss_model_changed import BetaCacheMissModelChanged as BetaCacheMissModelChanged from .beta_cache_miss_tools_changed import BetaCacheMissToolsChanged as BetaCacheMissToolsChanged from .beta_dream_memory_store_input import BetaDreamMemoryStoreInput as BetaDreamMemoryStoreInput from .beta_dream_model_config_param import BetaDreamModelConfigParam as BetaDreamModelConfigParam from .beta_fallback_credit_redeemed import BetaFallbackCreditRedeemed as BetaFallbackCreditRedeemed from .beta_fallback_refusal_trigger import BetaFallbackRefusalTrigger as BetaFallbackRefusalTrigger from .beta_json_output_format_param import BetaJSONOutputFormatParam as BetaJSONOutputFormatParam from .beta_mcp_tool_use_block_param import BetaMCPToolUseBlockParam as BetaMCPToolUseBlockParam from .beta_raw_message_stream_event import BetaRawMessageStreamEvent as BetaRawMessageStreamEvent from .beta_server_tool_caller_param import BetaServerToolCallerParam as BetaServerToolCallerParam from .beta_tool_bash_20241022_param import BetaToolBash20241022Param as BetaToolBash20241022Param from .beta_tool_bash_20250124_param import BetaToolBash20250124Param as BetaToolBash20250124Param from .beta_user_profile_trust_grant import BetaUserProfileTrustGrant as BetaUserProfileTrustGrant from .beta_advisor_tool_result_block import BetaAdvisorToolResultBlock as BetaAdvisorToolResultBlock from .beta_advisor_tool_result_error import BetaAdvisorToolResultError as BetaAdvisorToolResultError from .beta_base64_image_source_param import BetaBase64ImageSourceParam as BetaBase64ImageSourceParam from .beta_cache_miss_system_changed import BetaCacheMissSystemChanged as BetaCacheMissSystemChanged from .beta_managed_agents_delta_type import BetaManagedAgentsDeltaType as BetaManagedAgentsDeltaType from .beta_managed_agents_deployment import BetaManagedAgentsDeployment as BetaManagedAgentsDeployment from .beta_managed_agents_effort_low import BetaManagedAgentsEffortLow as BetaManagedAgentsEffortLow from .beta_managed_agents_effort_max import BetaManagedAgentsEffortMax as BetaManagedAgentsEffortMax from .beta_managed_agents_multiagent import BetaManagedAgentsMultiagent as BetaManagedAgentsMultiagent from .beta_search_result_block_param import BetaSearchResultBlockParam as BetaSearchResultBlockParam from .beta_self_hosted_config_params import BetaSelfHostedConfigParams as BetaSelfHostedConfigParams from .beta_advisor_result_block_param import BetaAdvisorResultBlockParam as BetaAdvisorResultBlockParam from .beta_compaction_iteration_usage import BetaCompactionIterationUsage as BetaCompactionIterationUsage from .beta_content_block_source_param import BetaContentBlockSourceParam as BetaContentBlockSourceParam from .beta_dream_sessions_input_param import BetaDreamSessionsInputParam as BetaDreamSessionsInputParam from .beta_file_document_source_param import BetaFileDocumentSourceParam as BetaFileDocumentSourceParam from .beta_input_tokens_trigger_param import BetaInputTokensTriggerParam as BetaInputTokensTriggerParam from .beta_managed_agents_custom_tool import BetaManagedAgentsCustomTool as BetaManagedAgentsCustomTool from .beta_managed_agents_delta_event import BetaManagedAgentsDeltaEvent as BetaManagedAgentsDeltaEvent from .beta_managed_agents_effort_high import BetaManagedAgentsEffortHigh as BetaManagedAgentsEffortHigh from .beta_managed_agents_mcp_toolset import BetaManagedAgentsMCPToolset as BetaManagedAgentsMCPToolset from .beta_managed_agents_model_param import BetaManagedAgentsModelParam as BetaManagedAgentsModelParam from .beta_managed_agents_start_event import BetaManagedAgentsStartEvent as BetaManagedAgentsStartEvent from .beta_memory_tool_20250818_param import BetaMemoryTool20250818Param as BetaMemoryTool20250818Param from .beta_tool_reference_block_param import BetaToolReferenceBlockParam as BetaToolReferenceBlockParam from .beta_unrestricted_network_param import BetaUnrestrictedNetworkParam as BetaUnrestrictedNetworkParam from .beta_advisor_tool_20260301_param import BetaAdvisorTool20260301Param as BetaAdvisorTool20260301Param from .beta_cache_miss_messages_changed import BetaCacheMissMessagesChanged as BetaCacheMissMessagesChanged from .beta_code_execution_output_block import BetaCodeExecutionOutputBlock as BetaCodeExecutionOutputBlock from .beta_code_execution_result_block import BetaCodeExecutionResultBlock as BetaCodeExecutionResultBlock from .beta_compact_20260112_edit_param import BetaCompact20260112EditParam as BetaCompact20260112EditParam from .beta_context_management_response import BetaContextManagementResponse as BetaContextManagementResponse from .beta_environment_delete_response import BetaEnvironmentDeleteResponse as BetaEnvironmentDeleteResponse from .beta_fallback_credit_not_applied import BetaFallbackCreditNotApplied as BetaFallbackCreditNotApplied from .beta_fallback_credit_token_param import BetaFallbackCreditTokenParam as BetaFallbackCreditTokenParam from .beta_managed_agents_agent_params import BetaManagedAgentsAgentParams as BetaManagedAgentsAgentParams from .beta_managed_agents_custom_skill import BetaManagedAgentsCustomSkill as BetaManagedAgentsCustomSkill from .beta_managed_agents_effort_xhigh import BetaManagedAgentsEffortXhigh as BetaManagedAgentsEffortXhigh from .beta_managed_agents_memory_store import BetaManagedAgentsMemoryStore as BetaManagedAgentsMemoryStore from .beta_managed_agents_model_config import BetaManagedAgentsModelConfig as BetaManagedAgentsModelConfig from .beta_managed_agents_skill_params import BetaManagedAgentsSkillParams as BetaManagedAgentsSkillParams from .beta_managed_agents_trigger_type import BetaManagedAgentsTriggerType as BetaManagedAgentsTriggerType from .beta_server_tool_caller_20260120 import BetaServerToolCaller20260120 as BetaServerToolCaller20260120 from .beta_server_tool_use_block_param import BetaServerToolUseBlockParam as BetaServerToolUseBlockParam from .beta_user_profile_enrollment_url import BetaUserProfileEnrollmentURL as BetaUserProfileEnrollmentURL from .beta_web_fetch_tool_result_block import BetaWebFetchToolResultBlock as BetaWebFetchToolResultBlock from .beta_citation_char_location_param import BetaCitationCharLocationParam as BetaCitationCharLocationParam from .beta_citation_page_location_param import BetaCitationPageLocationParam as BetaCitationPageLocationParam from .beta_container_upload_block_param import BetaContainerUploadBlockParam as BetaContainerUploadBlockParam from .beta_managed_agents_deleted_vault import BetaManagedAgentsDeletedVault as BetaManagedAgentsDeletedVault from .beta_managed_agents_delta_content import BetaManagedAgentsDeltaContent as BetaManagedAgentsDeltaContent from .beta_managed_agents_effort_medium import BetaManagedAgentsEffortMedium as BetaManagedAgentsEffortMedium from .beta_managed_agents_session_agent import BetaManagedAgentsSessionAgent as BetaManagedAgentsSessionAgent from .beta_managed_agents_session_stats import BetaManagedAgentsSessionStats as BetaManagedAgentsSessionStats from .beta_managed_agents_session_usage import BetaManagedAgentsSessionUsage as BetaManagedAgentsSessionUsage from .beta_memory_tool_20250818_command import BetaMemoryTool20250818Command as BetaMemoryTool20250818Command from .beta_raw_content_block_stop_event import BetaRawContentBlockStopEvent as BetaRawContentBlockStopEvent from .beta_request_document_block_param import BetaRequestDocumentBlockParam as BetaRequestDocumentBlockParam from .beta_web_search_tool_result_block import BetaWebSearchToolResultBlock as BetaWebSearchToolResultBlock from .beta_web_search_tool_result_error import BetaWebSearchToolResultError as BetaWebSearchToolResultError from .beta_advisor_redacted_result_block import BetaAdvisorRedactedResultBlock as BetaAdvisorRedactedResultBlock from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam as BetaCacheControlEphemeralParam from .beta_context_management_capability import BetaContextManagementCapability as BetaContextManagementCapability from .beta_managed_agents_deployment_run import BetaManagedAgentsDeploymentRun as BetaManagedAgentsDeploymentRun from .beta_mcp_tool_default_config_param import BetaMCPToolDefaultConfigParam as BetaMCPToolDefaultConfigParam from .beta_raw_content_block_delta_event import BetaRawContentBlockDeltaEvent as BetaRawContentBlockDeltaEvent from .beta_raw_content_block_start_event import BetaRawContentBlockStartEvent as BetaRawContentBlockStartEvent from .beta_redacted_thinking_block_param import BetaRedactedThinkingBlockParam as BetaRedactedThinkingBlockParam from .beta_thinking_config_enabled_param import BetaThinkingConfigEnabledParam as BetaThinkingConfigEnabledParam from .beta_tool_search_tool_result_block import BetaToolSearchToolResultBlock as BetaToolSearchToolResultBlock from .beta_tool_search_tool_result_error import BetaToolSearchToolResultError as BetaToolSearchToolResultError from .beta_web_fetch_tool_20250910_param import BetaWebFetchTool20250910Param as BetaWebFetchTool20250910Param from .beta_web_fetch_tool_20260209_param import BetaWebFetchTool20260209Param as BetaWebFetchTool20260209Param from .beta_web_fetch_tool_20260309_param import BetaWebFetchTool20260309Param as BetaWebFetchTool20260309Param from .beta_web_fetch_tool_20260318_param import BetaWebFetchTool20260318Param as BetaWebFetchTool20260318Param from .beta_web_search_result_block_param import BetaWebSearchResultBlockParam as BetaWebSearchResultBlockParam from .beta_compaction_content_block_delta import BetaCompactionContentBlockDelta as BetaCompactionContentBlockDelta from .beta_dream_memory_store_input_param import BetaDreamMemoryStoreInputParam as BetaDreamMemoryStoreInputParam from .beta_managed_agents_agent_reference import BetaManagedAgentsAgentReference as BetaManagedAgentsAgentReference from .beta_managed_agents_anthropic_skill import BetaManagedAgentsAnthropicSkill as BetaManagedAgentsAnthropicSkill from .beta_managed_agents_branch_checkout import BetaManagedAgentsBranchCheckout as BetaManagedAgentsBranchCheckout from .beta_managed_agents_commit_checkout import BetaManagedAgentsCommitCheckout as BetaManagedAgentsCommitCheckout from .beta_managed_agents_deleted_session import BetaManagedAgentsDeletedSession as BetaManagedAgentsDeletedSession from .beta_managed_agents_mcp_tool_config import BetaManagedAgentsMCPToolConfig as BetaManagedAgentsMCPToolConfig from .beta_managed_agents_schedule_params import BetaManagedAgentsScheduleParams as BetaManagedAgentsScheduleParams from .beta_managed_agents_trigger_context import BetaManagedAgentsTriggerContext as BetaManagedAgentsTriggerContext from .beta_thinking_config_adaptive_param import BetaThinkingConfigAdaptiveParam as BetaThinkingConfigAdaptiveParam from .beta_thinking_config_disabled_param import BetaThinkingConfigDisabledParam as BetaThinkingConfigDisabledParam from .beta_web_search_tool_20250305_param import BetaWebSearchTool20250305Param as BetaWebSearchTool20250305Param from .beta_web_search_tool_20260209_param import BetaWebSearchTool20260209Param as BetaWebSearchTool20260209Param from .beta_web_search_tool_20260318_param import BetaWebSearchTool20260318Param as BetaWebSearchTool20260318Param from .beta_advisor_message_iteration_usage import BetaAdvisorMessageIterationUsage as BetaAdvisorMessageIterationUsage from .beta_advisor_tool_result_block_param import BetaAdvisorToolResultBlockParam as BetaAdvisorToolResultBlockParam from .beta_advisor_tool_result_error_param import BetaAdvisorToolResultErrorParam as BetaAdvisorToolResultErrorParam from .beta_citation_content_block_location import BetaCitationContentBlockLocation as BetaCitationContentBlockLocation from .beta_citation_search_result_location import BetaCitationSearchResultLocation as BetaCitationSearchResultLocation from .beta_context_management_config_param import BetaContextManagementConfigParam as BetaContextManagementConfigParam from .beta_managed_agents_effort_low_param import BetaManagedAgentsEffortLowParam as BetaManagedAgentsEffortLowParam from .beta_managed_agents_effort_max_param import BetaManagedAgentsEffortMaxParam as BetaManagedAgentsEffortMaxParam from .beta_tool_text_editor_20241022_param import BetaToolTextEditor20241022Param as BetaToolTextEditor20241022Param from .beta_tool_text_editor_20250124_param import BetaToolTextEditor20250124Param as BetaToolTextEditor20250124Param from .beta_tool_text_editor_20250429_param import BetaToolTextEditor20250429Param as BetaToolTextEditor20250429Param from .beta_tool_text_editor_20250728_param import BetaToolTextEditor20250728Param as BetaToolTextEditor20250728Param from .beta_bash_code_execution_output_block import BetaBashCodeExecutionOutputBlock as BetaBashCodeExecutionOutputBlock from .beta_bash_code_execution_result_block import BetaBashCodeExecutionResultBlock as BetaBashCodeExecutionResultBlock from .beta_code_execution_tool_result_block import BetaCodeExecutionToolResultBlock as BetaCodeExecutionToolResultBlock from .beta_code_execution_tool_result_error import BetaCodeExecutionToolResultError as BetaCodeExecutionToolResultError from .beta_fallback_message_iteration_usage import ( BetaFallbackMessageIterationUsage as BetaFallbackMessageIterationUsage, ) from .beta_managed_agents_agent_tool_config import BetaManagedAgentsAgentToolConfig as BetaManagedAgentsAgentToolConfig from .beta_managed_agents_always_ask_policy import BetaManagedAgentsAlwaysAskPolicy as BetaManagedAgentsAlwaysAskPolicy from .beta_managed_agents_deployment_status import ( BetaManagedAgentsDeploymentStatus as BetaManagedAgentsDeploymentStatus, ) from .beta_managed_agents_effort_high_param import BetaManagedAgentsEffortHighParam as BetaManagedAgentsEffortHighParam from .beta_managed_agents_multiagent_params import ( BetaManagedAgentsMultiagentParams as BetaManagedAgentsMultiagentParams, ) from .beta_managed_agents_unknown_run_error import BetaManagedAgentsUnknownRunError as BetaManagedAgentsUnknownRunError from .beta_request_tool_removal_block_param import BetaRequestToolRemovalBlockParam as BetaRequestToolRemovalBlockParam from .beta_tool_change_tool_reference_param import BetaToolChangeToolReferenceParam as BetaToolChangeToolReferenceParam from .beta_tool_computer_use_20241022_param import BetaToolComputerUse20241022Param as BetaToolComputerUse20241022Param from .beta_tool_computer_use_20250124_param import BetaToolComputerUse20250124Param as BetaToolComputerUse20250124Param from .beta_tool_computer_use_20251124_param import BetaToolComputerUse20251124Param as BetaToolComputerUse20251124Param from .beta_web_fetch_tool_result_error_code import BetaWebFetchToolResultErrorCode as BetaWebFetchToolResultErrorCode from .beta_webhook_agent_created_event_data import BetaWebhookAgentCreatedEventData as BetaWebhookAgentCreatedEventData from .beta_webhook_agent_deleted_event_data import BetaWebhookAgentDeletedEventData as BetaWebhookAgentDeletedEventData from .beta_webhook_agent_updated_event_data import BetaWebhookAgentUpdatedEventData as BetaWebhookAgentUpdatedEventData from .beta_webhook_session_idled_event_data import BetaWebhookSessionIdledEventData as BetaWebhookSessionIdledEventData from .beta_webhook_vault_created_event_data import BetaWebhookVaultCreatedEventData as BetaWebhookVaultCreatedEventData from .beta_webhook_vault_deleted_event_data import BetaWebhookVaultDeletedEventData as BetaWebhookVaultDeletedEventData from .beta_code_execution_output_block_param import ( BetaCodeExecutionOutputBlockParam as BetaCodeExecutionOutputBlockParam, ) from .beta_code_execution_result_block_param import ( BetaCodeExecutionResultBlockParam as BetaCodeExecutionResultBlockParam, ) from .beta_input_tokens_clear_at_least_param import BetaInputTokensClearAtLeastParam as BetaInputTokensClearAtLeastParam from .beta_managed_agents_custom_tool_params import ( BetaManagedAgentsCustomToolParams as BetaManagedAgentsCustomToolParams, ) from .beta_managed_agents_effort_xhigh_param import ( BetaManagedAgentsEffortXhighParam as BetaManagedAgentsEffortXhighParam, ) from .beta_managed_agents_mcp_toolset_params import ( BetaManagedAgentsMCPToolsetParams as BetaManagedAgentsMCPToolsetParams, ) from .beta_memory_tool_20250818_view_command import ( BetaMemoryTool20250818ViewCommand as BetaMemoryTool20250818ViewCommand, ) from .beta_request_tool_addition_block_param import ( BetaRequestToolAdditionBlockParam as BetaRequestToolAdditionBlockParam, ) from .beta_server_tool_caller_20260120_param import ( BetaServerToolCaller20260120Param as BetaServerToolCaller20260120Param, ) from .beta_web_fetch_tool_result_block_param import BetaWebFetchToolResultBlockParam as BetaWebFetchToolResultBlockParam from .beta_web_fetch_tool_result_error_block import BetaWebFetchToolResultErrorBlock as BetaWebFetchToolResultErrorBlock from .beta_web_search_tool_result_error_code import BetaWebSearchToolResultErrorCode as BetaWebSearchToolResultErrorCode from .beta_webhook_agent_archived_event_data import ( BetaWebhookAgentArchivedEventData as BetaWebhookAgentArchivedEventData, ) from .beta_webhook_vault_archived_event_data import ( BetaWebhookVaultArchivedEventData as BetaWebhookVaultArchivedEventData, ) from .beta_clear_thinking_20251015_edit_param import ( BetaClearThinking20251015EditParam as BetaClearThinking20251015EditParam, ) from .beta_code_execution_tool_20250522_param import ( BetaCodeExecutionTool20250522Param as BetaCodeExecutionTool20250522Param, ) from .beta_code_execution_tool_20250825_param import ( BetaCodeExecutionTool20250825Param as BetaCodeExecutionTool20250825Param, ) from .beta_code_execution_tool_20260120_param import ( BetaCodeExecutionTool20260120Param as BetaCodeExecutionTool20260120Param, ) from .beta_code_execution_tool_20260521_param import ( BetaCodeExecutionTool20260521Param as BetaCodeExecutionTool20260521Param, ) from .beta_content_block_source_content_param import ( BetaContentBlockSourceContentParam as BetaContentBlockSourceContentParam, ) from .beta_managed_agents_always_allow_policy import ( BetaManagedAgentsAlwaysAllowPolicy as BetaManagedAgentsAlwaysAllowPolicy, ) from .beta_managed_agents_custom_skill_params import ( BetaManagedAgentsCustomSkillParams as BetaManagedAgentsCustomSkillParams, ) from .beta_managed_agents_effort_medium_param import ( BetaManagedAgentsEffortMediumParam as BetaManagedAgentsEffortMediumParam, ) from .beta_managed_agents_model_config_params import ( BetaManagedAgentsModelConfigParams as BetaManagedAgentsModelConfigParams, ) from .beta_managed_agents_start_event_preview import ( BetaManagedAgentsStartEventPreview as BetaManagedAgentsStartEventPreview, ) from .beta_web_search_tool_result_block_param import ( BetaWebSearchToolResultBlockParam as BetaWebSearchToolResultBlockParam, ) from .beta_webhook_session_created_event_data import ( BetaWebhookSessionCreatedEventData as BetaWebhookSessionCreatedEventData, ) from .beta_webhook_session_deleted_event_data import ( BetaWebhookSessionDeletedEventData as BetaWebhookSessionDeletedEventData, ) from .beta_webhook_session_pending_event_data import ( BetaWebhookSessionPendingEventData as BetaWebhookSessionPendingEventData, ) from .beta_webhook_session_running_event_data import ( BetaWebhookSessionRunningEventData as BetaWebhookSessionRunningEventData, ) from .beta_webhook_session_updated_event_data import ( BetaWebhookSessionUpdatedEventData as BetaWebhookSessionUpdatedEventData, ) from .beta_advisor_redacted_result_block_param import ( BetaAdvisorRedactedResultBlockParam as BetaAdvisorRedactedResultBlockParam, ) from .beta_clear_tool_uses_20250919_edit_param import ( BetaClearToolUses20250919EditParam as BetaClearToolUses20250919EditParam, ) from .beta_managed_agents_cache_creation_usage import ( BetaManagedAgentsCacheCreationUsage as BetaManagedAgentsCacheCreationUsage, ) from .beta_managed_agents_deleted_memory_store import ( BetaManagedAgentsDeletedMemoryStore as BetaManagedAgentsDeletedMemoryStore, ) from .beta_managed_agents_file_resource_config import ( BetaManagedAgentsFileResourceConfig as BetaManagedAgentsFileResourceConfig, ) from .beta_managed_agents_file_resource_params import ( BetaManagedAgentsFileResourceParams as BetaManagedAgentsFileResourceParams, ) from .beta_managed_agents_session_thread_agent import ( BetaManagedAgentsSessionThreadAgent as BetaManagedAgentsSessionThreadAgent, ) from .beta_managed_agents_system_content_block import ( BetaManagedAgentsSystemContentBlock as BetaManagedAgentsSystemContentBlock, ) from .beta_managed_agents_system_message_event import ( BetaManagedAgentsSystemMessageEvent as BetaManagedAgentsSystemMessageEvent, ) from .beta_memory_tool_20250818_create_command import ( BetaMemoryTool20250818CreateCommand as BetaMemoryTool20250818CreateCommand, ) from .beta_memory_tool_20250818_delete_command import ( BetaMemoryTool20250818DeleteCommand as BetaMemoryTool20250818DeleteCommand, ) from .beta_memory_tool_20250818_insert_command import ( BetaMemoryTool20250818InsertCommand as BetaMemoryTool20250818InsertCommand, ) from .beta_memory_tool_20250818_rename_command import ( BetaMemoryTool20250818RenameCommand as BetaMemoryTool20250818RenameCommand, ) from .beta_mid_conversation_system_block_param import ( BetaMidConversationSystemBlockParam as BetaMidConversationSystemBlockParam, ) from .beta_request_mcp_tool_result_block_param import ( BetaRequestMCPToolResultBlockParam as BetaRequestMCPToolResultBlockParam, ) from .beta_tool_search_tool_result_block_param import ( BetaToolSearchToolResultBlockParam as BetaToolSearchToolResultBlockParam, ) from .beta_tool_search_tool_result_error_param import ( BetaToolSearchToolResultErrorParam as BetaToolSearchToolResultErrorParam, ) from .beta_web_search_tool_request_error_param import ( BetaWebSearchToolRequestErrorParam as BetaWebSearchToolRequestErrorParam, ) from .beta_webhook_session_archived_event_data import ( BetaWebhookSessionArchivedEventData as BetaWebhookSessionArchivedEventData, ) from .beta_citations_web_search_result_location import ( BetaCitationsWebSearchResultLocation as BetaCitationsWebSearchResultLocation, ) from .beta_managed_agents_agent_message_preview import ( BetaManagedAgentsAgentMessagePreview as BetaManagedAgentsAgentMessagePreview, ) from .beta_managed_agents_agent_toolset20260401 import ( BetaManagedAgentsAgentToolset20260401 as BetaManagedAgentsAgentToolset20260401, ) from .beta_managed_agents_branch_checkout_param import ( BetaManagedAgentsBranchCheckoutParam as BetaManagedAgentsBranchCheckoutParam, ) from .beta_managed_agents_commit_checkout_param import ( BetaManagedAgentsCommitCheckoutParam as BetaManagedAgentsCommitCheckoutParam, ) from .beta_managed_agents_session_updated_event import ( BetaManagedAgentsSessionUpdatedEvent as BetaManagedAgentsSessionUpdatedEvent, ) from .beta_managed_agents_url_mcp_server_params import ( BetaManagedAgentsURLMCPServerParams as BetaManagedAgentsURLMCPServerParams, ) from .beta_tool_change_mcp_tool_reference_param import ( BetaToolChangeMCPToolReferenceParam as BetaToolChangeMCPToolReferenceParam, ) from .beta_tool_search_tool_bm25_20251119_param import ( BetaToolSearchToolBm25_20251119Param as BetaToolSearchToolBm25_20251119Param, ) from .beta_tool_search_tool_search_result_block import ( BetaToolSearchToolSearchResultBlock as BetaToolSearchToolSearchResultBlock, ) from .beta_web_search_tool_result_block_content import ( BetaWebSearchToolResultBlockContent as BetaWebSearchToolResultBlockContent, ) from .beta_webhook_deployment_paused_event_data import ( BetaWebhookDeploymentPausedEventData as BetaWebhookDeploymentPausedEventData, ) from .beta_bash_code_execution_tool_result_block import ( BetaBashCodeExecutionToolResultBlock as BetaBashCodeExecutionToolResultBlock, ) from .beta_bash_code_execution_tool_result_error import ( BetaBashCodeExecutionToolResultError as BetaBashCodeExecutionToolResultError, ) from .beta_cache_miss_previous_message_not_found import ( BetaCacheMissPreviousMessageNotFound as BetaCacheMissPreviousMessageNotFound, ) from .beta_citation_content_block_location_param import ( BetaCitationContentBlockLocationParam as BetaCitationContentBlockLocationParam, ) from .beta_citation_search_result_location_param import ( BetaCitationSearchResultLocationParam as BetaCitationSearchResultLocationParam, ) from .beta_clear_thinking_20251015_edit_response import ( BetaClearThinking20251015EditResponse as BetaClearThinking20251015EditResponse, ) from .beta_code_execution_tool_result_error_code import ( BetaCodeExecutionToolResultErrorCode as BetaCodeExecutionToolResultErrorCode, ) from .beta_encrypted_code_execution_result_block import ( BetaEncryptedCodeExecutionResultBlock as BetaEncryptedCodeExecutionResultBlock, ) from .beta_managed_agents_agent_thinking_preview import ( BetaManagedAgentsAgentThinkingPreview as BetaManagedAgentsAgentThinkingPreview, ) from .beta_managed_agents_anthropic_skill_params import ( BetaManagedAgentsAnthropicSkillParams as BetaManagedAgentsAnthropicSkillParams, ) from .beta_managed_agents_manual_trigger_context import ( BetaManagedAgentsManualTriggerContext as BetaManagedAgentsManualTriggerContext, ) from .beta_managed_agents_mcp_tool_config_params import ( BetaManagedAgentsMCPToolConfigParams as BetaManagedAgentsMCPToolConfigParams, ) from .beta_managed_agents_multiagent_self_params import ( BetaManagedAgentsMultiagentSelfParams as BetaManagedAgentsMultiagentSelfParams, ) from .beta_managed_agents_user_tool_result_event import ( BetaManagedAgentsUserToolResultEvent as BetaManagedAgentsUserToolResultEvent, ) from .beta_tool_search_tool_regex_20251119_param import ( BetaToolSearchToolRegex20251119Param as BetaToolSearchToolRegex20251119Param, ) from .beta_webhook_deployment_created_event_data import ( BetaWebhookDeploymentCreatedEventData as BetaWebhookDeploymentCreatedEventData, ) from .beta_webhook_deployment_deleted_event_data import ( BetaWebhookDeploymentDeletedEventData as BetaWebhookDeploymentDeletedEventData, ) from .beta_webhook_deployment_updated_event_data import ( BetaWebhookDeploymentUpdatedEventData as BetaWebhookDeploymentUpdatedEventData, ) from .beta_bash_code_execution_output_block_param import ( BetaBashCodeExecutionOutputBlockParam as BetaBashCodeExecutionOutputBlockParam, ) from .beta_bash_code_execution_result_block_param import ( BetaBashCodeExecutionResultBlockParam as BetaBashCodeExecutionResultBlockParam, ) from .beta_clear_tool_uses_20250919_edit_response import ( BetaClearToolUses20250919EditResponse as BetaClearToolUses20250919EditResponse, ) from .beta_code_execution_tool_result_block_param import ( BetaCodeExecutionToolResultBlockParam as BetaCodeExecutionToolResultBlockParam, ) from .beta_code_execution_tool_result_error_param import ( BetaCodeExecutionToolResultErrorParam as BetaCodeExecutionToolResultErrorParam, ) from .beta_managed_agents_always_ask_policy_param import ( BetaManagedAgentsAlwaysAskPolicyParam as BetaManagedAgentsAlwaysAskPolicyParam, ) from .beta_managed_agents_session_resource_config import ( BetaManagedAgentsSessionResourceConfig as BetaManagedAgentsSessionResourceConfig, ) from .beta_webhook_deployment_archived_event_data import ( BetaWebhookDeploymentArchivedEventData as BetaWebhookDeploymentArchivedEventData, ) from .beta_webhook_deployment_unpaused_event_data import ( BetaWebhookDeploymentUnpausedEventData as BetaWebhookDeploymentUnpausedEventData, ) from .beta_webhook_environment_created_event_data import ( BetaWebhookEnvironmentCreatedEventData as BetaWebhookEnvironmentCreatedEventData, ) from .beta_webhook_environment_deleted_event_data import ( BetaWebhookEnvironmentDeletedEventData as BetaWebhookEnvironmentDeletedEventData, ) from .beta_webhook_environment_updated_event_data import ( BetaWebhookEnvironmentUpdatedEventData as BetaWebhookEnvironmentUpdatedEventData, ) from .beta_managed_agents_agent_archived_run_error import ( BetaManagedAgentsAgentArchivedRunError as BetaManagedAgentsAgentArchivedRunError, ) from .beta_managed_agents_agent_tool_config_params import ( BetaManagedAgentsAgentToolConfigParams as BetaManagedAgentsAgentToolConfigParams, ) from .beta_managed_agents_custom_tool_input_schema import ( BetaManagedAgentsCustomToolInputSchema as BetaManagedAgentsCustomToolInputSchema, ) from .beta_managed_agents_deployment_initial_event import ( BetaManagedAgentsDeploymentInitialEvent as BetaManagedAgentsDeploymentInitialEvent, ) from .beta_managed_agents_deployment_paused_reason import ( BetaManagedAgentsDeploymentPausedReason as BetaManagedAgentsDeploymentPausedReason, ) from .beta_managed_agents_file_not_found_run_error import ( BetaManagedAgentsFileNotFoundRunError as BetaManagedAgentsFileNotFoundRunError, ) from .beta_managed_agents_schedule_trigger_context import ( BetaManagedAgentsScheduleTriggerContext as BetaManagedAgentsScheduleTriggerContext, ) from .beta_managed_agents_vault_archived_run_error import ( BetaManagedAgentsVaultArchivedRunError as BetaManagedAgentsVaultArchivedRunError, ) from .beta_request_mcp_server_url_definition_param import ( BetaRequestMCPServerURLDefinitionParam as BetaRequestMCPServerURLDefinitionParam, ) from .beta_tool_change_mcp_toolset_reference_param import ( BetaToolChangeMCPToolsetReferenceParam as BetaToolChangeMCPToolsetReferenceParam, ) from .beta_web_fetch_tool_result_error_block_param import ( BetaWebFetchToolResultErrorBlockParam as BetaWebFetchToolResultErrorBlockParam, ) from .beta_webhook_environment_archived_event_data import ( BetaWebhookEnvironmentArchivedEventData as BetaWebhookEnvironmentArchivedEventData, ) from .beta_webhook_memory_store_created_event_data import ( BetaWebhookMemoryStoreCreatedEventData as BetaWebhookMemoryStoreCreatedEventData, ) from .beta_webhook_memory_store_deleted_event_data import ( BetaWebhookMemoryStoreDeletedEventData as BetaWebhookMemoryStoreDeletedEventData, ) from .beta_webhook_session_status_idled_event_data import ( BetaWebhookSessionStatusIdledEventData as BetaWebhookSessionStatusIdledEventData, ) from .beta_webhook_session_thread_idled_event_data import ( BetaWebhookSessionThreadIdledEventData as BetaWebhookSessionThreadIdledEventData, ) from .beta_code_execution_tool_result_block_content import ( BetaCodeExecutionToolResultBlockContent as BetaCodeExecutionToolResultBlockContent, ) from .beta_count_tokens_context_management_response import ( BetaCountTokensContextManagementResponse as BetaCountTokensContextManagementResponse, ) from .beta_managed_agents_always_allow_policy_param import ( BetaManagedAgentsAlwaysAllowPolicyParam as BetaManagedAgentsAlwaysAllowPolicyParam, ) from .beta_managed_agents_mcp_server_url_definition import ( BetaManagedAgentsMCPServerURLDefinition as BetaManagedAgentsMCPServerURLDefinition, ) from .beta_managed_agents_skill_not_found_run_error import ( BetaManagedAgentsSkillNotFoundRunError as BetaManagedAgentsSkillNotFoundRunError, ) from .beta_managed_agents_vault_not_found_run_error import ( BetaManagedAgentsVaultNotFoundRunError as BetaManagedAgentsVaultNotFoundRunError, ) from .beta_memory_tool_20250818_str_replace_command import ( BetaMemoryTool20250818StrReplaceCommand as BetaMemoryTool20250818StrReplaceCommand, ) from .beta_webhook_deployment_run_failed_event_data import ( BetaWebhookDeploymentRunFailedEventData as BetaWebhookDeploymentRunFailedEventData, ) from .beta_webhook_memory_store_archived_event_data import ( BetaWebhookMemoryStoreArchivedEventData as BetaWebhookMemoryStoreArchivedEventData, ) from .beta_citation_web_search_result_location_param import ( BetaCitationWebSearchResultLocationParam as BetaCitationWebSearchResultLocationParam, ) from .beta_managed_agents_mcp_toolset_default_config import ( BetaManagedAgentsMCPToolsetDefaultConfig as BetaManagedAgentsMCPToolsetDefaultConfig, ) from .beta_managed_agents_session_agent_update_param import ( BetaManagedAgentsSessionAgentUpdateParam as BetaManagedAgentsSessionAgentUpdateParam, ) from .beta_managed_agents_system_content_block_param import ( BetaManagedAgentsSystemContentBlockParam as BetaManagedAgentsSystemContentBlockParam, ) from .beta_webhook_deployment_run_started_event_data import ( BetaWebhookDeploymentRunStartedEventData as BetaWebhookDeploymentRunStartedEventData, ) from .beta_webhook_session_thread_created_event_data import ( BetaWebhookSessionThreadCreatedEventData as BetaWebhookSessionThreadCreatedEventData, ) from .beta_managed_agents_agent_with_overrides_params import ( BetaManagedAgentsAgentWithOverridesParams as BetaManagedAgentsAgentWithOverridesParams, ) from .beta_managed_agents_memory_store_resource_param import ( BetaManagedAgentsMemoryStoreResourceParam as BetaManagedAgentsMemoryStoreResourceParam, ) from .beta_managed_agents_outcome_evaluation_resource import ( BetaManagedAgentsOutcomeEvaluationResource as BetaManagedAgentsOutcomeEvaluationResource, ) from .beta_tool_search_tool_search_result_block_param import ( BetaToolSearchToolSearchResultBlockParam as BetaToolSearchToolSearchResultBlockParam, ) from .beta_webhook_session_requires_action_event_data import ( BetaWebhookSessionRequiresActionEventData as BetaWebhookSessionRequiresActionEventData, ) from .beta_bash_code_execution_tool_result_block_param import ( BetaBashCodeExecutionToolResultBlockParam as BetaBashCodeExecutionToolResultBlockParam, ) from .beta_bash_code_execution_tool_result_error_param import ( BetaBashCodeExecutionToolResultErrorParam as BetaBashCodeExecutionToolResultErrorParam, ) from .beta_encrypted_code_execution_result_block_param import ( BetaEncryptedCodeExecutionResultBlockParam as BetaEncryptedCodeExecutionResultBlockParam, ) from .beta_managed_agents_agent_toolset20260401_params import ( BetaManagedAgentsAgentToolset20260401Params as BetaManagedAgentsAgentToolset20260401Params, ) from .beta_managed_agents_agent_toolset_default_config import ( BetaManagedAgentsAgentToolsetDefaultConfig as BetaManagedAgentsAgentToolsetDefaultConfig, ) from .beta_managed_agents_mcp_egress_blocked_run_error import ( BetaManagedAgentsMCPEgressBlockedRunError as BetaManagedAgentsMCPEgressBlockedRunError, ) from .beta_managed_agents_memory_store_resource_config import ( BetaManagedAgentsMemoryStoreResourceConfig as BetaManagedAgentsMemoryStoreResourceConfig, ) from .beta_managed_agents_workspace_archived_run_error import ( BetaManagedAgentsWorkspaceArchivedRunError as BetaManagedAgentsWorkspaceArchivedRunError, ) from .beta_request_mcp_server_tool_configuration_param import ( BetaRequestMCPServerToolConfigurationParam as BetaRequestMCPServerToolConfigurationParam, ) from .beta_webhook_deployment_run_succeeded_event_data import ( BetaWebhookDeploymentRunSucceededEventData as BetaWebhookDeploymentRunSucceededEventData, ) from .beta_webhook_vault_credential_created_event_data import ( BetaWebhookVaultCredentialCreatedEventData as BetaWebhookVaultCredentialCreatedEventData, ) from .beta_webhook_vault_credential_deleted_event_data import ( BetaWebhookVaultCredentialDeletedEventData as BetaWebhookVaultCredentialDeletedEventData, ) from .beta_managed_agents_deployment_user_message_event import ( BetaManagedAgentsDeploymentUserMessageEvent as BetaManagedAgentsDeploymentUserMessageEvent, ) from .beta_text_editor_code_execution_tool_result_block import ( BetaTextEditorCodeExecutionToolResultBlock as BetaTextEditorCodeExecutionToolResultBlock, ) from .beta_text_editor_code_execution_tool_result_error import ( BetaTextEditorCodeExecutionToolResultError as BetaTextEditorCodeExecutionToolResultError, ) from .beta_text_editor_code_execution_view_result_block import ( BetaTextEditorCodeExecutionViewResultBlock as BetaTextEditorCodeExecutionViewResultBlock, ) from .beta_webhook_session_status_terminated_event_data import ( BetaWebhookSessionStatusTerminatedEventData as BetaWebhookSessionStatusTerminatedEventData, ) from .beta_webhook_session_thread_terminated_event_data import ( BetaWebhookSessionThreadTerminatedEventData as BetaWebhookSessionThreadTerminatedEventData, ) from .beta_webhook_vault_credential_archived_event_data import ( BetaWebhookVaultCredentialArchivedEventData as BetaWebhookVaultCredentialArchivedEventData, ) from .beta_managed_agents_custom_tool_input_schema_param import ( BetaManagedAgentsCustomToolInputSchemaParam as BetaManagedAgentsCustomToolInputSchemaParam, ) from .beta_managed_agents_deployment_paused_reason_error import ( BetaManagedAgentsDeploymentPausedReasonError as BetaManagedAgentsDeploymentPausedReasonError, ) from .beta_managed_agents_environment_archived_run_error import ( BetaManagedAgentsEnvironmentArchivedRunError as BetaManagedAgentsEnvironmentArchivedRunError, ) from .beta_managed_agents_error_deployment_paused_reason import ( BetaManagedAgentsErrorDeploymentPausedReason as BetaManagedAgentsErrorDeploymentPausedReason, ) from .beta_managed_agents_multiagent_roster_entry_params import ( BetaManagedAgentsMultiagentRosterEntryParams as BetaManagedAgentsMultiagentRosterEntryParams, ) from .beta_managed_agents_session_multiagent_coordinator import ( BetaManagedAgentsSessionMultiagentCoordinator as BetaManagedAgentsSessionMultiagentCoordinator, ) from .beta_managed_agents_session_rate_limited_run_error import ( BetaManagedAgentsSessionRateLimitedRunError as BetaManagedAgentsSessionRateLimitedRunError, ) from .beta_webhook_session_status_rescheduled_event_data import ( BetaWebhookSessionStatusRescheduledEventData as BetaWebhookSessionStatusRescheduledEventData, ) from .beta_webhook_session_status_run_started_event_data import ( BetaWebhookSessionStatusRunStartedEventData as BetaWebhookSessionStatusRunStartedEventData, ) from .beta_managed_agents_deployment_initial_event_params import ( BetaManagedAgentsDeploymentInitialEventParams as BetaManagedAgentsDeploymentInitialEventParams, ) from .beta_managed_agents_deployment_system_message_event import ( BetaManagedAgentsDeploymentSystemMessageEvent as BetaManagedAgentsDeploymentSystemMessageEvent, ) from .beta_managed_agents_environment_not_found_run_error import ( BetaManagedAgentsEnvironmentNotFoundRunError as BetaManagedAgentsEnvironmentNotFoundRunError, ) from .beta_managed_agents_manual_deployment_paused_reason import ( BetaManagedAgentsManualDeploymentPausedReason as BetaManagedAgentsManualDeploymentPausedReason, ) from .beta_managed_agents_memory_store_archived_run_error import ( BetaManagedAgentsMemoryStoreArchivedRunError as BetaManagedAgentsMemoryStoreArchivedRunError, ) from .beta_managed_agents_organization_disabled_run_error import ( BetaManagedAgentsOrganizationDisabledRunError as BetaManagedAgentsOrganizationDisabledRunError, ) from .beta_text_editor_code_execution_create_result_block import ( BetaTextEditorCodeExecutionCreateResultBlock as BetaTextEditorCodeExecutionCreateResultBlock, ) from .beta_managed_agents_agent_toolset20260401_bash_input import ( BetaManagedAgentsAgentToolset20260401BashInput as BetaManagedAgentsAgentToolset20260401BashInput, ) from .beta_managed_agents_agent_toolset20260401_edit_input import ( BetaManagedAgentsAgentToolset20260401EditInput as BetaManagedAgentsAgentToolset20260401EditInput, ) from .beta_managed_agents_agent_toolset20260401_glob_input import ( BetaManagedAgentsAgentToolset20260401GlobInput as BetaManagedAgentsAgentToolset20260401GlobInput, ) from .beta_managed_agents_agent_toolset20260401_grep_input import ( BetaManagedAgentsAgentToolset20260401GrepInput as BetaManagedAgentsAgentToolset20260401GrepInput, ) from .beta_managed_agents_agent_toolset20260401_read_input import ( BetaManagedAgentsAgentToolset20260401ReadInput as BetaManagedAgentsAgentToolset20260401ReadInput, ) from .beta_managed_agents_agent_toolset20260401_write_input import ( BetaManagedAgentsAgentToolset20260401WriteInput as BetaManagedAgentsAgentToolset20260401WriteInput, ) from .beta_managed_agents_github_repository_resource_config import ( BetaManagedAgentsGitHubRepositoryResourceConfig as BetaManagedAgentsGitHubRepositoryResourceConfig, ) from .beta_managed_agents_github_repository_resource_params import ( BetaManagedAgentsGitHubRepositoryResourceParams as BetaManagedAgentsGitHubRepositoryResourceParams, ) from .beta_managed_agents_mcp_toolset_default_config_params import ( BetaManagedAgentsMCPToolsetDefaultConfigParams as BetaManagedAgentsMCPToolsetDefaultConfigParams, ) from .beta_web_search_tool_result_block_param_content_param import ( BetaWebSearchToolResultBlockParamContentParam as BetaWebSearchToolResultBlockParamContentParam, ) from .beta_managed_agents_agent_toolset_default_config_params import ( BetaManagedAgentsAgentToolsetDefaultConfigParams as BetaManagedAgentsAgentToolsetDefaultConfigParams, ) from .beta_managed_agents_session_creation_rejected_run_error import ( BetaManagedAgentsSessionCreationRejectedRunError as BetaManagedAgentsSessionCreationRejectedRunError, ) from .beta_text_editor_code_execution_tool_result_block_param import ( BetaTextEditorCodeExecutionToolResultBlockParam as BetaTextEditorCodeExecutionToolResultBlockParam, ) from .beta_text_editor_code_execution_tool_result_error_param import ( BetaTextEditorCodeExecutionToolResultErrorParam as BetaTextEditorCodeExecutionToolResultErrorParam, ) from .beta_text_editor_code_execution_view_result_block_param import ( BetaTextEditorCodeExecutionViewResultBlockParam as BetaTextEditorCodeExecutionViewResultBlockParam, ) from .beta_webhook_vault_credential_refresh_failed_event_data import ( BetaWebhookVaultCredentialRefreshFailedEventData as BetaWebhookVaultCredentialRefreshFailedEventData, ) from .beta_managed_agents_deployment_user_define_outcome_event import ( BetaManagedAgentsDeploymentUserDefineOutcomeEvent as BetaManagedAgentsDeploymentUserDefineOutcomeEvent, ) from .beta_managed_agents_session_resource_not_found_run_error import ( BetaManagedAgentsSessionResourceNotFoundRunError as BetaManagedAgentsSessionResourceNotFoundRunError, ) from .beta_text_editor_code_execution_str_replace_result_block import ( BetaTextEditorCodeExecutionStrReplaceResultBlock as BetaTextEditorCodeExecutionStrReplaceResultBlock, ) from .beta_webhook_session_outcome_evaluation_ended_event_data import ( BetaWebhookSessionOutcomeEvaluationEndedEventData as BetaWebhookSessionOutcomeEvaluationEndedEventData, ) from .beta_code_execution_tool_result_block_param_content_param import ( BetaCodeExecutionToolResultBlockParamContentParam as BetaCodeExecutionToolResultBlockParamContentParam, ) from .beta_text_editor_code_execution_create_result_block_param import ( BetaTextEditorCodeExecutionCreateResultBlockParam as BetaTextEditorCodeExecutionCreateResultBlockParam, ) from .beta_managed_agents_unknown_deployment_paused_reason_error import ( BetaManagedAgentsUnknownDeploymentPausedReasonError as BetaManagedAgentsUnknownDeploymentPausedReasonError, ) from .beta_text_editor_code_execution_str_replace_result_block_param import ( BetaTextEditorCodeExecutionStrReplaceResultBlockParam as BetaTextEditorCodeExecutionStrReplaceResultBlockParam, ) from .beta_managed_agents_self_hosted_resources_unsupported_run_error import ( BetaManagedAgentsSelfHostedResourcesUnsupportedRunError as BetaManagedAgentsSelfHostedResourcesUnsupportedRunError, ) from .beta_managed_agents_agent_archived_deployment_paused_reason_error import ( BetaManagedAgentsAgentArchivedDeploymentPausedReasonError as BetaManagedAgentsAgentArchivedDeploymentPausedReasonError, ) from .beta_managed_agents_file_not_found_deployment_paused_reason_error import ( BetaManagedAgentsFileNotFoundDeploymentPausedReasonError as BetaManagedAgentsFileNotFoundDeploymentPausedReasonError, ) from .beta_managed_agents_vault_archived_deployment_paused_reason_error import ( BetaManagedAgentsVaultArchivedDeploymentPausedReasonError as BetaManagedAgentsVaultArchivedDeploymentPausedReasonError, ) from .beta_managed_agents_skill_not_found_deployment_paused_reason_error import ( BetaManagedAgentsSkillNotFoundDeploymentPausedReasonError as BetaManagedAgentsSkillNotFoundDeploymentPausedReasonError, ) from .beta_managed_agents_vault_not_found_deployment_paused_reason_error import ( BetaManagedAgentsVaultNotFoundDeploymentPausedReasonError as BetaManagedAgentsVaultNotFoundDeploymentPausedReasonError, ) from .beta_managed_agents_mcp_egress_blocked_deployment_paused_reason_error import ( BetaManagedAgentsMCPEgressBlockedDeploymentPausedReasonError as BetaManagedAgentsMCPEgressBlockedDeploymentPausedReasonError, ) from .beta_managed_agents_workspace_archived_deployment_paused_reason_error import ( BetaManagedAgentsWorkspaceArchivedDeploymentPausedReasonError as BetaManagedAgentsWorkspaceArchivedDeploymentPausedReasonError, ) from .beta_managed_agents_environment_archived_deployment_paused_reason_error import ( BetaManagedAgentsEnvironmentArchivedDeploymentPausedReasonError as BetaManagedAgentsEnvironmentArchivedDeploymentPausedReasonError, ) from .beta_managed_agents_environment_not_found_deployment_paused_reason_error import ( BetaManagedAgentsEnvironmentNotFoundDeploymentPausedReasonError as BetaManagedAgentsEnvironmentNotFoundDeploymentPausedReasonError, ) from .beta_managed_agents_memory_store_archived_deployment_paused_reason_error import ( BetaManagedAgentsMemoryStoreArchivedDeploymentPausedReasonError as BetaManagedAgentsMemoryStoreArchivedDeploymentPausedReasonError, ) from .beta_managed_agents_organization_disabled_deployment_paused_reason_error import ( BetaManagedAgentsOrganizationDisabledDeploymentPausedReasonError as BetaManagedAgentsOrganizationDisabledDeploymentPausedReasonError, ) from .beta_managed_agents_session_resource_not_found_deployment_paused_reason_error import ( BetaManagedAgentsSessionResourceNotFoundDeploymentPausedReasonError as BetaManagedAgentsSessionResourceNotFoundDeploymentPausedReasonError, ) from .beta_managed_agents_self_hosted_resources_unsupported_deployment_paused_reason_error import ( BetaManagedAgentsSelfHostedResourcesUnsupportedDeploymentPausedReasonError as BetaManagedAgentsSelfHostedResourcesUnsupportedDeploymentPausedReasonError, ) anthropic-sdk-python-0.120.2/src/anthropic/types/beta/agent_create_params.py000066400000000000000000000057201523216435200271430ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Union, Iterable, Optional from typing_extensions import Required, Annotated, TypeAlias, TypedDict from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam from .beta_managed_agents_model_param import BetaManagedAgentsModelParam from .beta_managed_agents_skill_params import BetaManagedAgentsSkillParams from .beta_managed_agents_multiagent_params import BetaManagedAgentsMultiagentParams from .beta_managed_agents_custom_tool_params import BetaManagedAgentsCustomToolParams from .beta_managed_agents_mcp_toolset_params import BetaManagedAgentsMCPToolsetParams from .beta_managed_agents_model_config_params import BetaManagedAgentsModelConfigParams from .beta_managed_agents_url_mcp_server_params import BetaManagedAgentsURLMCPServerParams from .beta_managed_agents_agent_toolset20260401_params import BetaManagedAgentsAgentToolset20260401Params __all__ = ["AgentCreateParams", "Model", "Tool"] class AgentCreateParams(TypedDict, total=False): model: Required[Model] """Model identifier. Accepts the [model string](https://platform.claude.com/docs/en/about-claude/models/overview#latest-models-comparison), e.g. `claude-opus-4-6`, or a `model_config` object for additional configuration control """ name: Required[str] """Human-readable name for the agent.""" description: Optional[str] """Description of what the agent does.""" mcp_servers: Iterable[BetaManagedAgentsURLMCPServerParams] """MCP servers this agent connects to. Maximum 20. Names must be unique within the array. Every server must be referenced by an `mcp_toolset` in `tools`; unreferenced servers are rejected. See the [MCP connector guide](https://platform.claude.com/docs/en/managed-agents/mcp-connector). """ metadata: Dict[str, str] """Arbitrary key-value metadata. Maximum 16 pairs, keys up to 64 chars, values up to 512 chars. """ multiagent: Optional[BetaManagedAgentsMultiagentParams] """ A coordinator topology: the session's primary thread orchestrates work by spawning session threads, each running an agent drawn from the `agents` roster. """ skills: Iterable[BetaManagedAgentsSkillParams] """Skills available to the agent.""" system: Optional[str] """System prompt for the agent.""" tools: Iterable[Tool] """Tool configurations available to the agent. Maximum of 128 tools across all toolsets allowed. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" Model: TypeAlias = Union[BetaManagedAgentsModelParam, BetaManagedAgentsModelConfigParams] Tool: TypeAlias = Union[ BetaManagedAgentsAgentToolset20260401Params, BetaManagedAgentsMCPToolsetParams, BetaManagedAgentsCustomToolParams ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/agent_list_params.py000066400000000000000000000022151523216435200266470ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Union from datetime import datetime from typing_extensions import Annotated, TypedDict from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam __all__ = ["AgentListParams"] class AgentListParams(TypedDict, total=False): created_at_gte: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[gte]", format="iso8601")] """Return agents created at or after this time (inclusive).""" created_at_lte: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[lte]", format="iso8601")] """Return agents created at or before this time (inclusive).""" include_archived: bool """Include archived agents in results. Defaults to false.""" limit: int """Maximum results per page. Default 20, maximum 100.""" page: str """Opaque pagination cursor from a previous response.""" betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/agent_retrieve_params.py000066400000000000000000000012171523216435200275220ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List from typing_extensions import Annotated, TypedDict from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam __all__ = ["AgentRetrieveParams"] class AgentRetrieveParams(TypedDict, total=False): version: int """Agent version. Omit for the most recent version. Must be at least 1 if specified. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/agent_update_params.py000066400000000000000000000073401523216435200271620ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Union, Iterable, Optional from typing_extensions import Annotated, TypeAlias, TypedDict from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam from .beta_managed_agents_model_param import BetaManagedAgentsModelParam from .beta_managed_agents_skill_params import BetaManagedAgentsSkillParams from .beta_managed_agents_multiagent_params import BetaManagedAgentsMultiagentParams from .beta_managed_agents_custom_tool_params import BetaManagedAgentsCustomToolParams from .beta_managed_agents_mcp_toolset_params import BetaManagedAgentsMCPToolsetParams from .beta_managed_agents_model_config_params import BetaManagedAgentsModelConfigParams from .beta_managed_agents_url_mcp_server_params import BetaManagedAgentsURLMCPServerParams from .beta_managed_agents_agent_toolset20260401_params import BetaManagedAgentsAgentToolset20260401Params __all__ = ["AgentUpdateParams", "Model", "Tool"] class AgentUpdateParams(TypedDict, total=False): description: Optional[str] """Description. Omit to preserve; send empty string or null to clear.""" mcp_servers: Optional[Iterable[BetaManagedAgentsURLMCPServerParams]] """MCP servers. Full replacement. Omit to preserve; send empty array or `null` to clear. Names must be unique. Maximum 20. Every server must be referenced by an `mcp_toolset` in the agent's resulting `tools`; unreferenced servers are rejected. See the [MCP connector guide](https://platform.claude.com/docs/en/managed-agents/mcp-connector). """ metadata: Optional[Dict[str, Optional[str]]] """Metadata patch. Set a key to a string to upsert it, or to null to delete it. Omit the field to preserve. The stored bag is limited to 16 keys (up to 64 chars each) with values up to 512 chars. """ model: Model """Model identifier. Accepts the [model string](https://platform.claude.com/docs/en/about-claude/models/overview#latest-models-comparison), e.g. `claude-opus-4-6`, or a `model_config` object for additional configuration control. Omit to preserve. Cannot be cleared. """ multiagent: Optional[BetaManagedAgentsMultiagentParams] """ A coordinator topology: the session's primary thread orchestrates work by spawning session threads, each running an agent drawn from the `agents` roster. """ name: str """Human-readable name. Must be non-empty. Omit to preserve. Cannot be cleared.""" skills: Optional[Iterable[BetaManagedAgentsSkillParams]] """Skills. Full replacement. Omit to preserve; send empty array or null to clear.""" system: Optional[str] """System prompt. Omit to preserve; send empty string or null to clear.""" tools: Optional[Iterable[Tool]] """Tool configurations available to the agent. Full replacement. Omit to preserve; send empty array or null to clear. Maximum of 128 tools across all toolsets allowed. """ version: int """The agent's current version, used to prevent concurrent overwrites. Obtain this value from a create or retrieve response. Must be at least 1 if specified. When supplied, the request fails if it does not match the server's current version; omit to apply the update unconditionally. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" Model: TypeAlias = Union[BetaManagedAgentsModelParam, BetaManagedAgentsModelConfigParams] Tool: TypeAlias = Union[ BetaManagedAgentsAgentToolset20260401Params, BetaManagedAgentsMCPToolsetParams, BetaManagedAgentsCustomToolParams ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/agents/000077500000000000000000000000001523216435200240625ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/agents/__init__.py000066400000000000000000000003031523216435200261670ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from .version_list_params import VersionListParams as VersionListParams anthropic-sdk-python-0.120.2/src/anthropic/types/beta/agents/version_list_params.py000066400000000000000000000012251523216435200305170ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List from typing_extensions import Annotated, TypedDict from ...._utils import PropertyInfo from ...anthropic_beta_param import AnthropicBetaParam __all__ = ["VersionListParams"] class VersionListParams(TypedDict, total=False): limit: int """Maximum results per page. Default 20, maximum 100.""" page: str """Opaque pagination cursor.""" betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_advisor_message_iteration_usage.py000066400000000000000000000022101523216435200325560ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ..model import Model from ..._models import BaseModel from .beta_cache_creation import BetaCacheCreation __all__ = ["BetaAdvisorMessageIterationUsage"] class BetaAdvisorMessageIterationUsage(BaseModel): """Token usage for an advisor sub-inference iteration.""" cache_creation: Optional[BetaCacheCreation] = None """Breakdown of cached tokens by TTL""" cache_creation_input_tokens: int """The number of input tokens used to create the cache entry.""" cache_read_input_tokens: int """The number of input tokens read from the cache.""" input_tokens: int """The number of input tokens which were used.""" model: Model """The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. """ output_tokens: int """The number of output tokens which were used.""" type: Literal["advisor_message"] """Usage for an advisor sub-inference iteration""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_advisor_redacted_result_block.py000066400000000000000000000011621523216435200322200ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaAdvisorRedactedResultBlock"] class BetaAdvisorRedactedResultBlock(BaseModel): encrypted_content: str """Opaque blob containing the advisor's output. Round-trip verbatim; do not inspect or modify. """ stop_reason: Optional[str] = None """ The advisor sub-inference's stop reason (same values as the top-level message `stop_reason`). """ type: Literal["advisor_redacted_result"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_advisor_redacted_result_block_param.py000066400000000000000000000010331523216435200333750ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaAdvisorRedactedResultBlockParam"] class BetaAdvisorRedactedResultBlockParam(TypedDict, total=False): encrypted_content: Required[str] """Opaque blob produced by a prior response; must be round-tripped verbatim.""" type: Required[Literal["advisor_redacted_result"]] stop_reason: Optional[str] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_advisor_result_block.py000066400000000000000000000011361523216435200303660ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaAdvisorResultBlock"] class BetaAdvisorResultBlock(BaseModel): stop_reason: Optional[str] = None """ The advisor sub-inference's stop reason (same values as the top-level message `stop_reason`). `max_tokens` indicates the advisor's output was truncated at the tool's `max_tokens` value or the advisor model's policy cap. """ text: str type: Literal["advisor_result"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_advisor_result_block_param.py000066400000000000000000000006411523216435200315460ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaAdvisorResultBlockParam"] class BetaAdvisorResultBlockParam(TypedDict, total=False): text: Required[str] type: Required[Literal["advisor_result"]] stop_reason: Optional[str] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_advisor_tool_20260301_param.py000066400000000000000000000045241523216435200310140ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Literal, Required, TypedDict from ..model_param import ModelParam from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaAdvisorTool20260301Param"] class BetaAdvisorTool20260301Param(TypedDict, total=False): model: Required[ModelParam] """The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. """ name: Required[Literal["advisor"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["advisor_20260301"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" caching: Optional[BetaCacheControlEphemeralParam] """Caching for the advisor's own prompt. When set, each advisor call writes a cache entry at the given TTL so subsequent calls in the same conversation read the stable prefix. When omitted, the advisor prompt is not cached. """ defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ max_tokens: Optional[int] """Bounds the advisor's total output (thinking + text) per call. When the advisor hits this cap, the returned advisor_result or advisor_redacted_result block carries stop_reason='max_tokens', and a truncation note is appended to the advice text the worker model sees (inside the encrypted blob in redacted mode). When set, the server also emits a remaining-tokens budget block in the advisor's prompt so the advisor self-shapes toward the cap. When omitted, the advisor model's default output cap applies and no budget block is emitted. """ max_uses: Optional[int] """Maximum number of times the tool can be used in the API request.""" strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_advisor_tool_result_block.py000066400000000000000000000013001523216435200314140ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Literal, TypeAlias from ..._models import BaseModel from .beta_advisor_result_block import BetaAdvisorResultBlock from .beta_advisor_tool_result_error import BetaAdvisorToolResultError from .beta_advisor_redacted_result_block import BetaAdvisorRedactedResultBlock __all__ = ["BetaAdvisorToolResultBlock", "Content"] Content: TypeAlias = Union[BetaAdvisorToolResultError, BetaAdvisorResultBlock, BetaAdvisorRedactedResultBlock] class BetaAdvisorToolResultBlock(BaseModel): content: Content tool_use_id: str type: Literal["advisor_tool_result"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_advisor_tool_result_block_param.py000066400000000000000000000020341523216435200326010ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .beta_advisor_result_block_param import BetaAdvisorResultBlockParam from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam from .beta_advisor_tool_result_error_param import BetaAdvisorToolResultErrorParam from .beta_advisor_redacted_result_block_param import BetaAdvisorRedactedResultBlockParam __all__ = ["BetaAdvisorToolResultBlockParam", "Content"] Content: TypeAlias = Union[ BetaAdvisorToolResultErrorParam, BetaAdvisorResultBlockParam, BetaAdvisorRedactedResultBlockParam ] class BetaAdvisorToolResultBlockParam(TypedDict, total=False): content: Required[Content] tool_use_id: Required[str] type: Required[Literal["advisor_tool_result"]] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_advisor_tool_result_error.py000066400000000000000000000010071523216435200314570ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaAdvisorToolResultError"] class BetaAdvisorToolResultError(BaseModel): error_code: Literal[ "max_uses_exceeded", "prompt_too_long", "too_many_requests", "overloaded", "unavailable", "execution_time_exceeded", "model_not_found", ] type: Literal["advisor_tool_result_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_advisor_tool_result_error_param.py000066400000000000000000000011671523216435200326460ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaAdvisorToolResultErrorParam"] class BetaAdvisorToolResultErrorParam(TypedDict, total=False): error_code: Required[ Literal[ "max_uses_exceeded", "prompt_too_long", "too_many_requests", "overloaded", "unavailable", "execution_time_exceeded", "model_not_found", ] ] type: Required[Literal["advisor_tool_result_error"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_all_thinking_turns_param.py000066400000000000000000000004751523216435200312320ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaAllThinkingTurnsParam"] class BetaAllThinkingTurnsParam(TypedDict, total=False): type: Required[Literal["all"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_base64_image_source_param.py000066400000000000000000000013441523216435200311360ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from typing_extensions import Literal, Required, Annotated, TypedDict from ..._types import Base64FileInput from ..._utils import PropertyInfo from ..._models import set_pydantic_config __all__ = ["BetaBase64ImageSourceParam"] class BetaBase64ImageSourceParam(TypedDict, total=False): data: Required[Annotated[Union[str, Base64FileInput], PropertyInfo(format="base64")]] media_type: Required[Literal["image/jpeg", "image/png", "image/gif", "image/webp"]] type: Required[Literal["base64"]] set_pydantic_config(BetaBase64ImageSourceParam, {"arbitrary_types_allowed": True}) anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_base64_pdf_block_param.py000066400000000000000000000004011523216435200304100ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from .beta_request_document_block_param import BetaRequestDocumentBlockParam BetaBase64PDFBlockParam = BetaRequestDocumentBlockParam anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_base64_pdf_source.py000066400000000000000000000005011523216435200274370ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaBase64PDFSource"] class BetaBase64PDFSource(BaseModel): data: str media_type: Literal["application/pdf"] type: Literal["base64"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_base64_pdf_source_param.py000066400000000000000000000012731523216435200306260ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from typing_extensions import Literal, Required, Annotated, TypedDict from ..._types import Base64FileInput from ..._utils import PropertyInfo from ..._models import set_pydantic_config __all__ = ["BetaBase64PDFSourceParam"] class BetaBase64PDFSourceParam(TypedDict, total=False): data: Required[Annotated[Union[str, Base64FileInput], PropertyInfo(format="base64")]] media_type: Required[Literal["application/pdf"]] type: Required[Literal["base64"]] set_pydantic_config(BetaBase64PDFSourceParam, {"arbitrary_types_allowed": True}) anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_bash_code_execution_output_block.py000066400000000000000000000005061523216435200327330ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaBashCodeExecutionOutputBlock"] class BetaBashCodeExecutionOutputBlock(BaseModel): file_id: str type: Literal["bash_code_execution_output"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_bash_code_execution_output_block_param.py000066400000000000000000000006101523216435200341070ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaBashCodeExecutionOutputBlockParam"] class BetaBashCodeExecutionOutputBlockParam(TypedDict, total=False): file_id: Required[str] type: Required[Literal["bash_code_execution_output"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_bash_code_execution_result_block.py000066400000000000000000000010151523216435200327050ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List from typing_extensions import Literal from ..._models import BaseModel from .beta_bash_code_execution_output_block import BetaBashCodeExecutionOutputBlock __all__ = ["BetaBashCodeExecutionResultBlock"] class BetaBashCodeExecutionResultBlock(BaseModel): content: List[BetaBashCodeExecutionOutputBlock] return_code: int stderr: str stdout: str type: Literal["bash_code_execution_result"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_bash_code_execution_result_block_param.py000066400000000000000000000012061523216435200340670ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Iterable from typing_extensions import Literal, Required, TypedDict from .beta_bash_code_execution_output_block_param import BetaBashCodeExecutionOutputBlockParam __all__ = ["BetaBashCodeExecutionResultBlockParam"] class BetaBashCodeExecutionResultBlockParam(TypedDict, total=False): content: Required[Iterable[BetaBashCodeExecutionOutputBlockParam]] return_code: Required[int] stderr: Required[str] stdout: Required[str] type: Required[Literal["bash_code_execution_result"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_bash_code_execution_tool_result_block.py000066400000000000000000000012611523216435200337450ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Literal, TypeAlias from ..._models import BaseModel from .beta_bash_code_execution_result_block import BetaBashCodeExecutionResultBlock from .beta_bash_code_execution_tool_result_error import BetaBashCodeExecutionToolResultError __all__ = ["BetaBashCodeExecutionToolResultBlock", "Content"] Content: TypeAlias = Union[BetaBashCodeExecutionToolResultError, BetaBashCodeExecutionResultBlock] class BetaBashCodeExecutionToolResultBlock(BaseModel): content: Content tool_use_id: str type: Literal["bash_code_execution_tool_result"] beta_bash_code_execution_tool_result_block_param.py000066400000000000000000000017671523216435200350610ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam from .beta_bash_code_execution_result_block_param import BetaBashCodeExecutionResultBlockParam from .beta_bash_code_execution_tool_result_error_param import BetaBashCodeExecutionToolResultErrorParam __all__ = ["BetaBashCodeExecutionToolResultBlockParam", "Content"] Content: TypeAlias = Union[BetaBashCodeExecutionToolResultErrorParam, BetaBashCodeExecutionResultBlockParam] class BetaBashCodeExecutionToolResultBlockParam(TypedDict, total=False): content: Required[Content] tool_use_id: Required[str] type: Required[Literal["bash_code_execution_tool_result"]] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_bash_code_execution_tool_result_error.py000066400000000000000000000007341523216435200340100ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaBashCodeExecutionToolResultError"] class BetaBashCodeExecutionToolResultError(BaseModel): error_code: Literal[ "invalid_tool_input", "unavailable", "too_many_requests", "execution_time_exceeded", "output_file_too_large" ] type: Literal["bash_code_execution_tool_result_error"] beta_bash_code_execution_tool_result_error_param.py000066400000000000000000000010641523216435200351060ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaBashCodeExecutionToolResultErrorParam"] class BetaBashCodeExecutionToolResultErrorParam(TypedDict, total=False): error_code: Required[ Literal[ "invalid_tool_input", "unavailable", "too_many_requests", "execution_time_exceeded", "output_file_too_large" ] ] type: Required[Literal["bash_code_execution_tool_result_error"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_cache_control_ephemeral_param.py000066400000000000000000000012211523216435200321470ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaCacheControlEphemeralParam"] class BetaCacheControlEphemeralParam(TypedDict, total=False): type: Required[Literal["ephemeral"]] ttl: Literal["5m", "1h"] """The time-to-live for the cache control breakpoint. This may be one the following values: - `5m`: 5 minutes - `1h`: 1 hour Defaults to `5m`. See [prompt caching pricing](https://platform.claude.com/docs/en/build-with-claude/prompt-caching) for details. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_cache_creation.py000066400000000000000000000006401523216435200270750ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from ..._models import BaseModel __all__ = ["BetaCacheCreation"] class BetaCacheCreation(BaseModel): ephemeral_1h_input_tokens: int """The number of input tokens used to create the 1 hour cache entry.""" ephemeral_5m_input_tokens: int """The number of input tokens used to create the 5 minute cache entry.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_cache_miss_messages_changed.py000066400000000000000000000007231523216435200316060ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaCacheMissMessagesChanged"] class BetaCacheMissMessagesChanged(BaseModel): cache_missed_input_tokens: int """ Approximate number of input tokens that would have been read from cache had the prefix matched the previous request. """ type: Literal["messages_changed"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_cache_miss_model_changed.py000066400000000000000000000007121523216435200310750ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaCacheMissModelChanged"] class BetaCacheMissModelChanged(BaseModel): cache_missed_input_tokens: int """ Approximate number of input tokens that would have been read from cache had the prefix matched the previous request. """ type: Literal["model_changed"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_cache_miss_previous_message_not_found.py000066400000000000000000000004741523216435200337640ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaCacheMissPreviousMessageNotFound"] class BetaCacheMissPreviousMessageNotFound(BaseModel): type: Literal["previous_message_not_found"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_cache_miss_system_changed.py000066400000000000000000000007151523216435200313240ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaCacheMissSystemChanged"] class BetaCacheMissSystemChanged(BaseModel): cache_missed_input_tokens: int """ Approximate number of input tokens that would have been read from cache had the prefix matched the previous request. """ type: Literal["system_changed"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_cache_miss_tools_changed.py000066400000000000000000000007121523216435200311350ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaCacheMissToolsChanged"] class BetaCacheMissToolsChanged(BaseModel): cache_missed_input_tokens: int """ Approximate number of input tokens that would have been read from cache had the prefix matched the previous request. """ type: Literal["tools_changed"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_cache_miss_unavailable.py000066400000000000000000000004251523216435200306100ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaCacheMissUnavailable"] class BetaCacheMissUnavailable(BaseModel): type: Literal["unavailable"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_capability_support.py000066400000000000000000000005201523216435200300600ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from ..._models import BaseModel __all__ = ["BetaCapabilitySupport"] class BetaCapabilitySupport(BaseModel): """Indicates whether a capability is supported.""" supported: bool """Whether this capability is supported by the model.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_citation_char_location.py000066400000000000000000000007421523216435200306500ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaCitationCharLocation"] class BetaCitationCharLocation(BaseModel): cited_text: str document_index: int document_title: Optional[str] = None end_char_index: int file_id: Optional[str] = None start_char_index: int type: Literal["char_location"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_citation_char_location_param.py000066400000000000000000000010421523216435200320220ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaCitationCharLocationParam"] class BetaCitationCharLocationParam(TypedDict, total=False): cited_text: Required[str] document_index: Required[int] document_title: Required[Optional[str]] end_char_index: Required[int] start_char_index: Required[int] type: Required[Literal["char_location"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_citation_config.py000066400000000000000000000003231523216435200273030ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from ..._models import BaseModel __all__ = ["BetaCitationConfig"] class BetaCitationConfig(BaseModel): enabled: bool anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_citation_content_block_location.py000066400000000000000000000022611523216435200325550ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaCitationContentBlockLocation"] class BetaCitationContentBlockLocation(BaseModel): cited_text: str """The full text of the cited block range, concatenated. Always equals the contents of `content[start_block_index:end_block_index]` joined together. The text block is the minimal citable unit; this field is never a substring of a single block. Not counted toward output tokens, and not counted toward input tokens when sent back in subsequent turns. """ document_index: int document_title: Optional[str] = None end_block_index: int """ Exclusive 0-based end index of the cited block range in the source's `content` array. Always greater than `start_block_index`; a single-block citation has `end_block_index = start_block_index + 1`. """ file_id: Optional[str] = None start_block_index: int """0-based index of the first cited block in the source's `content` array.""" type: Literal["content_block_location"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_citation_content_block_location_param.py000066400000000000000000000023611523216435200337360ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaCitationContentBlockLocationParam"] class BetaCitationContentBlockLocationParam(TypedDict, total=False): cited_text: Required[str] """The full text of the cited block range, concatenated. Always equals the contents of `content[start_block_index:end_block_index]` joined together. The text block is the minimal citable unit; this field is never a substring of a single block. Not counted toward output tokens, and not counted toward input tokens when sent back in subsequent turns. """ document_index: Required[int] document_title: Required[Optional[str]] end_block_index: Required[int] """ Exclusive 0-based end index of the cited block range in the source's `content` array. Always greater than `start_block_index`; a single-block citation has `end_block_index = start_block_index + 1`. """ start_block_index: Required[int] """0-based index of the first cited block in the source's `content` array.""" type: Required[Literal["content_block_location"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_citation_page_location.py000066400000000000000000000007441523216435200306510ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaCitationPageLocation"] class BetaCitationPageLocation(BaseModel): cited_text: str document_index: int document_title: Optional[str] = None end_page_number: int file_id: Optional[str] = None start_page_number: int type: Literal["page_location"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_citation_page_location_param.py000066400000000000000000000010441523216435200320230ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaCitationPageLocationParam"] class BetaCitationPageLocationParam(TypedDict, total=False): cited_text: Required[str] document_index: Required[int] document_title: Required[Optional[str]] end_page_number: Required[int] start_page_number: Required[int] type: Required[Literal["page_location"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_citation_search_result_location.py000066400000000000000000000027061523216435200326000ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaCitationSearchResultLocation"] class BetaCitationSearchResultLocation(BaseModel): cited_text: str """The full text of the cited block range, concatenated. Always equals the contents of `content[start_block_index:end_block_index]` joined together. The text block is the minimal citable unit; this field is never a substring of a single block. Not counted toward output tokens, and not counted toward input tokens when sent back in subsequent turns. """ end_block_index: int """ Exclusive 0-based end index of the cited block range in the source's `content` array. Always greater than `start_block_index`; a single-block citation has `end_block_index = start_block_index + 1`. """ search_result_index: int """ 0-based index of the cited search result among all `search_result` content blocks in the request, in the order they appear across messages and tool results. Counted separately from `document_index`; server-side web search results are not included in this count. """ source: str start_block_index: int """0-based index of the first cited block in the source's `content` array.""" title: Optional[str] = None type: Literal["search_result_location"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_citation_search_result_location_param.py000066400000000000000000000030631523216435200337550ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaCitationSearchResultLocationParam"] class BetaCitationSearchResultLocationParam(TypedDict, total=False): cited_text: Required[str] """The full text of the cited block range, concatenated. Always equals the contents of `content[start_block_index:end_block_index]` joined together. The text block is the minimal citable unit; this field is never a substring of a single block. Not counted toward output tokens, and not counted toward input tokens when sent back in subsequent turns. """ end_block_index: Required[int] """ Exclusive 0-based end index of the cited block range in the source's `content` array. Always greater than `start_block_index`; a single-block citation has `end_block_index = start_block_index + 1`. """ search_result_index: Required[int] """ 0-based index of the cited search result among all `search_result` content blocks in the request, in the order they appear across messages and tool results. Counted separately from `document_index`; server-side web search results are not included in this count. """ source: Required[str] start_block_index: Required[int] """0-based index of the first cited block in the source's `content` array.""" title: Required[Optional[str]] type: Required[Literal["search_result_location"]] beta_citation_web_search_result_location_param.py000066400000000000000000000010151523216435200345260ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaCitationWebSearchResultLocationParam"] class BetaCitationWebSearchResultLocationParam(TypedDict, total=False): cited_text: Required[str] encrypted_index: Required[str] title: Required[Optional[str]] type: Required[Literal["web_search_result_location"]] url: Required[str] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_citations_config_param.py000066400000000000000000000004271523216435200306530ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import TypedDict __all__ = ["BetaCitationsConfigParam"] class BetaCitationsConfigParam(TypedDict, total=False): enabled: bool anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_citations_delta.py000066400000000000000000000020551523216435200273160ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Literal, Annotated, TypeAlias from ..._utils import PropertyInfo from ..._models import BaseModel from .beta_citation_char_location import BetaCitationCharLocation from .beta_citation_page_location import BetaCitationPageLocation from .beta_citation_content_block_location import BetaCitationContentBlockLocation from .beta_citation_search_result_location import BetaCitationSearchResultLocation from .beta_citations_web_search_result_location import BetaCitationsWebSearchResultLocation __all__ = ["BetaCitationsDelta", "Citation"] Citation: TypeAlias = Annotated[ Union[ BetaCitationCharLocation, BetaCitationPageLocation, BetaCitationContentBlockLocation, BetaCitationsWebSearchResultLocation, BetaCitationSearchResultLocation, ], PropertyInfo(discriminator="type"), ] class BetaCitationsDelta(BaseModel): citation: Citation type: Literal["citations_delta"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_citations_web_search_result_location.py000066400000000000000000000006661523216435200336230ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaCitationsWebSearchResultLocation"] class BetaCitationsWebSearchResultLocation(BaseModel): cited_text: str encrypted_index: str title: Optional[str] = None type: Literal["web_search_result_location"] url: str anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_clear_thinking_20251015_edit_param.py000066400000000000000000000014131523216435200322720ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from typing_extensions import Literal, Required, TypeAlias, TypedDict from .beta_thinking_turns_param import BetaThinkingTurnsParam from .beta_all_thinking_turns_param import BetaAllThinkingTurnsParam __all__ = ["BetaClearThinking20251015EditParam", "Keep"] Keep: TypeAlias = Union[BetaThinkingTurnsParam, BetaAllThinkingTurnsParam, Literal["all"]] class BetaClearThinking20251015EditParam(TypedDict, total=False): type: Required[Literal["clear_thinking_20251015"]] keep: Keep """Number of most recent assistant turns to keep thinking blocks for. Older turns will have their thinking blocks removed. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_clear_thinking_20251015_edit_response.py000066400000000000000000000010371523216435200330320ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaClearThinking20251015EditResponse"] class BetaClearThinking20251015EditResponse(BaseModel): cleared_input_tokens: int """Number of input tokens cleared by this edit.""" cleared_thinking_turns: int """Number of thinking turns that were cleared.""" type: Literal["clear_thinking_20251015"] """The type of context management edit applied.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_clear_tool_uses_20250919_edit_param.py000066400000000000000000000027151523216435200325150ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..._types import SequenceNotStr from .beta_tool_uses_keep_param import BetaToolUsesKeepParam from .beta_tool_uses_trigger_param import BetaToolUsesTriggerParam from .beta_input_tokens_trigger_param import BetaInputTokensTriggerParam from .beta_input_tokens_clear_at_least_param import BetaInputTokensClearAtLeastParam __all__ = ["BetaClearToolUses20250919EditParam", "Trigger"] Trigger: TypeAlias = Union[BetaInputTokensTriggerParam, BetaToolUsesTriggerParam] class BetaClearToolUses20250919EditParam(TypedDict, total=False): type: Required[Literal["clear_tool_uses_20250919"]] clear_at_least: Optional[BetaInputTokensClearAtLeastParam] """Minimum number of tokens that must be cleared when triggered. Context will only be modified if at least this many tokens can be removed. """ clear_tool_inputs: Union[bool, SequenceNotStr[str], None] """Whether to clear all tool inputs (bool) or specific tool inputs to clear (list)""" exclude_tools: Optional[SequenceNotStr[str]] """Tool names whose uses are preserved from clearing""" keep: BetaToolUsesKeepParam """Number of tool uses to retain in the conversation""" trigger: Trigger """Condition that triggers the context management strategy""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_clear_tool_uses_20250919_edit_response.py000066400000000000000000000010261523216435200332450ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaClearToolUses20250919EditResponse"] class BetaClearToolUses20250919EditResponse(BaseModel): cleared_input_tokens: int """Number of input tokens cleared by this edit.""" cleared_tool_uses: int """Number of tool uses that were cleared.""" type: Literal["clear_tool_uses_20250919"] """The type of context management edit applied.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_cloud_config.py000066400000000000000000000015141523216435200266020ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Literal, Annotated, TypeAlias from ..._utils import PropertyInfo from ..._models import BaseModel from .beta_packages import BetaPackages from .beta_limited_network import BetaLimitedNetwork from .beta_unrestricted_network import BetaUnrestrictedNetwork __all__ = ["BetaCloudConfig", "Networking"] Networking: TypeAlias = Annotated[ Union[BetaUnrestrictedNetwork, BetaLimitedNetwork], PropertyInfo(discriminator="type") ] class BetaCloudConfig(BaseModel): """`cloud` environment configuration.""" networking: Networking """Network configuration policy.""" packages: BetaPackages """Package manager configuration.""" type: Literal["cloud"] """Environment type""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_cloud_config_params.py000066400000000000000000000024571523216435200301540ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .beta_packages_params import BetaPackagesParams from .beta_limited_network_params import BetaLimitedNetworkParams from .beta_unrestricted_network_param import BetaUnrestrictedNetworkParam __all__ = ["BetaCloudConfigParams", "Networking"] Networking: TypeAlias = Union[BetaUnrestrictedNetworkParam, BetaLimitedNetworkParams] class BetaCloudConfigParams(TypedDict, total=False): """Request params for `cloud` environment configuration. Fields default to null; on update, omitted fields preserve the existing value. """ type: Required[Literal["cloud"]] """Environment type""" networking: Optional[Networking] """Network configuration policy. Omit on update to preserve the existing value.""" packages: Optional[BetaPackagesParams] """Specify packages (and optionally their versions) available in this environment. When versioning, use the version semantics relevant for the package manager, e.g. for `pip` use `package==1.0.0`. You are responsible for validating the package and version exist. Unversioned installs the latest. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_code_execution_output_block.py000066400000000000000000000004711523216435200317370ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaCodeExecutionOutputBlock"] class BetaCodeExecutionOutputBlock(BaseModel): file_id: str type: Literal["code_execution_output"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_code_execution_output_block_param.py000066400000000000000000000005731523216435200331220ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaCodeExecutionOutputBlockParam"] class BetaCodeExecutionOutputBlockParam(TypedDict, total=False): file_id: Required[str] type: Required[Literal["code_execution_output"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_code_execution_result_block.py000066400000000000000000000007631523216435200317210ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List from typing_extensions import Literal from ..._models import BaseModel from .beta_code_execution_output_block import BetaCodeExecutionOutputBlock __all__ = ["BetaCodeExecutionResultBlock"] class BetaCodeExecutionResultBlock(BaseModel): content: List[BetaCodeExecutionOutputBlock] return_code: int stderr: str stdout: str type: Literal["code_execution_result"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_code_execution_result_block_param.py000066400000000000000000000011541523216435200330740ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Iterable from typing_extensions import Literal, Required, TypedDict from .beta_code_execution_output_block_param import BetaCodeExecutionOutputBlockParam __all__ = ["BetaCodeExecutionResultBlockParam"] class BetaCodeExecutionResultBlockParam(TypedDict, total=False): content: Required[Iterable[BetaCodeExecutionOutputBlockParam]] return_code: Required[int] stderr: Required[str] stdout: Required[str] type: Required[Literal["code_execution_result"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_code_execution_tool_20250522_param.py000066400000000000000000000022101523216435200323340ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Literal, Required, TypedDict from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaCodeExecutionTool20250522Param"] class BetaCodeExecutionTool20250522Param(TypedDict, total=False): name: Required[Literal["code_execution"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["code_execution_20250522"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_code_execution_tool_20250825_param.py000066400000000000000000000022101523216435200323420ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Literal, Required, TypedDict from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaCodeExecutionTool20250825Param"] class BetaCodeExecutionTool20250825Param(TypedDict, total=False): name: Required[Literal["code_execution"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["code_execution_20250825"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_code_execution_tool_20260120_param.py000066400000000000000000000023601523216435200323350ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Literal, Required, TypedDict from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaCodeExecutionTool20260120Param"] class BetaCodeExecutionTool20260120Param(TypedDict, total=False): """ Code execution tool with REPL state persistence (daemon mode + gVisor checkpoint). """ name: Required[Literal["code_execution"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["code_execution_20260120"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_code_execution_tool_20260521_param.py000066400000000000000000000023041523216435200323400ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Literal, Required, TypedDict from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaCodeExecutionTool20260521Param"] class BetaCodeExecutionTool20260521Param(TypedDict, total=False): """Code execution tool with REPL state persistence.""" name: Required[Literal["code_execution"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["code_execution_20260521"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_code_execution_tool_result_block.py000066400000000000000000000010671523216435200327540ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel from .beta_code_execution_tool_result_block_content import BetaCodeExecutionToolResultBlockContent __all__ = ["BetaCodeExecutionToolResultBlock"] class BetaCodeExecutionToolResultBlock(BaseModel): content: BetaCodeExecutionToolResultBlockContent """Code execution result with encrypted stdout for PFC + web_search results.""" tool_use_id: str type: Literal["code_execution_tool_result"] beta_code_execution_tool_result_block_content.py000066400000000000000000000011661523216435200344270ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import TypeAlias from .beta_code_execution_result_block import BetaCodeExecutionResultBlock from .beta_code_execution_tool_result_error import BetaCodeExecutionToolResultError from .beta_encrypted_code_execution_result_block import BetaEncryptedCodeExecutionResultBlock __all__ = ["BetaCodeExecutionToolResultBlockContent"] BetaCodeExecutionToolResultBlockContent: TypeAlias = Union[ BetaCodeExecutionToolResultError, BetaCodeExecutionResultBlock, BetaEncryptedCodeExecutionResultBlock ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_code_execution_tool_result_block_param.py000066400000000000000000000016171523216435200341350ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam from .beta_code_execution_tool_result_block_param_content_param import BetaCodeExecutionToolResultBlockParamContentParam __all__ = ["BetaCodeExecutionToolResultBlockParam"] class BetaCodeExecutionToolResultBlockParam(TypedDict, total=False): content: Required[BetaCodeExecutionToolResultBlockParamContentParam] """Code execution result with encrypted stdout for PFC + web_search results.""" tool_use_id: Required[str] type: Required[Literal["code_execution_tool_result"]] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" beta_code_execution_tool_result_block_param_content_param.py000066400000000000000000000013361523216435200367660ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from typing_extensions import TypeAlias from .beta_code_execution_result_block_param import BetaCodeExecutionResultBlockParam from .beta_code_execution_tool_result_error_param import BetaCodeExecutionToolResultErrorParam from .beta_encrypted_code_execution_result_block_param import BetaEncryptedCodeExecutionResultBlockParam __all__ = ["BetaCodeExecutionToolResultBlockParamContentParam"] BetaCodeExecutionToolResultBlockParamContentParam: TypeAlias = Union[ BetaCodeExecutionToolResultErrorParam, BetaCodeExecutionResultBlockParam, BetaEncryptedCodeExecutionResultBlockParam ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_code_execution_tool_result_error.py000066400000000000000000000007151523216435200330120ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel from .beta_code_execution_tool_result_error_code import BetaCodeExecutionToolResultErrorCode __all__ = ["BetaCodeExecutionToolResultError"] class BetaCodeExecutionToolResultError(BaseModel): error_code: BetaCodeExecutionToolResultErrorCode type: Literal["code_execution_tool_result_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_code_execution_tool_result_error_code.py000066400000000000000000000005221523216435200340000ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal, TypeAlias __all__ = ["BetaCodeExecutionToolResultErrorCode"] BetaCodeExecutionToolResultErrorCode: TypeAlias = Literal[ "invalid_tool_input", "unavailable", "too_many_requests", "execution_time_exceeded" ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_code_execution_tool_result_error_param.py000066400000000000000000000010201523216435200341600ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict from .beta_code_execution_tool_result_error_code import BetaCodeExecutionToolResultErrorCode __all__ = ["BetaCodeExecutionToolResultErrorParam"] class BetaCodeExecutionToolResultErrorParam(TypedDict, total=False): error_code: Required[BetaCodeExecutionToolResultErrorCode] type: Required[Literal["code_execution_tool_result_error"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_compact_20260112_edit_param.py000066400000000000000000000015531523216435200307420ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict from .beta_input_tokens_trigger_param import BetaInputTokensTriggerParam __all__ = ["BetaCompact20260112EditParam"] class BetaCompact20260112EditParam(TypedDict, total=False): """ Automatically compact older context when reaching the configured trigger threshold. """ type: Required[Literal["compact_20260112"]] instructions: Optional[str] """Additional instructions for summarization.""" pause_after_compaction: bool """Whether to pause after compaction and return the compaction block to the user.""" trigger: Optional[BetaInputTokensTriggerParam] """When to trigger compaction. Defaults to 150000 input tokens.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_compaction_block.py000066400000000000000000000014741523216435200274620ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaCompactionBlock"] class BetaCompactionBlock(BaseModel): """A compaction block returned when autocompact is triggered. When content is None, it indicates the compaction failed to produce a valid summary (e.g., malformed output from the model). Clients may round-trip compaction blocks with null content; the server treats them as no-ops. """ content: Optional[str] = None """Summary of compacted content, or null if compaction failed""" encrypted_content: Optional[str] = None """Opaque metadata from prior compaction, to be round-tripped verbatim""" type: Literal["compaction"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_compaction_block_param.py000066400000000000000000000021601523216435200306330ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaCompactionBlockParam"] class BetaCompactionBlockParam(TypedDict, total=False): """A compaction block containing summary of previous context. Users should round-trip these blocks from responses to subsequent requests to maintain context across compaction boundaries. When content is None, the block represents a failed compaction. The server treats these as no-ops. Empty string content is not allowed. """ type: Required[Literal["compaction"]] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" content: Optional[str] """Summary of previously compacted content, or null if compaction failed""" encrypted_content: Optional[str] """Opaque metadata from prior compaction, to be round-tripped verbatim""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_compaction_content_block_delta.py000066400000000000000000000007421523216435200323620ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaCompactionContentBlockDelta"] class BetaCompactionContentBlockDelta(BaseModel): content: Optional[str] = None encrypted_content: Optional[str] = None """Opaque metadata from prior compaction, to be round-tripped verbatim""" type: Literal["compaction_delta"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_compaction_iteration_usage.py000066400000000000000000000016201523216435200315430ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ..._models import BaseModel from .beta_cache_creation import BetaCacheCreation __all__ = ["BetaCompactionIterationUsage"] class BetaCompactionIterationUsage(BaseModel): """Token usage for a compaction iteration.""" cache_creation: Optional[BetaCacheCreation] = None """Breakdown of cached tokens by TTL""" cache_creation_input_tokens: int """The number of input tokens used to create the cache entry.""" cache_read_input_tokens: int """The number of input tokens read from the cache.""" input_tokens: int """The number of input tokens which were used.""" output_tokens: int """The number of output tokens which were used.""" type: Literal["compaction"] """Usage for a compaction iteration""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_container.py000066400000000000000000000011611523216435200261270ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from datetime import datetime from ..._models import BaseModel from .beta_skill import BetaSkill __all__ = ["BetaContainer"] class BetaContainer(BaseModel): """ Information about the container used in the request (for the code execution tool) """ id: str """Identifier for the container used in this request""" expires_at: datetime """The time at which the container will expire.""" skills: Optional[List[BetaSkill]] = None """Skills loaded in the container""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_container_params.py000066400000000000000000000010331523216435200274700ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Iterable, Optional from typing_extensions import TypedDict from .beta_skill_params import BetaSkillParams __all__ = ["BetaContainerParams"] class BetaContainerParams(TypedDict, total=False): """Container parameters with skills to be loaded.""" id: Optional[str] """Container id""" skills: Optional[Iterable[BetaSkillParams]] """List of skills to load in the container""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_container_upload_block.py000066400000000000000000000005541523216435200306520ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaContainerUploadBlock"] class BetaContainerUploadBlock(BaseModel): """Response model for a file uploaded to the container.""" file_id: str type: Literal["container_upload"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_container_upload_block_param.py000066400000000000000000000014161523216435200320300ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaContainerUploadBlockParam"] class BetaContainerUploadBlockParam(TypedDict, total=False): """ A content block that represents a file to be uploaded to the container Files uploaded via this block will be available in the container's input directory. """ file_id: Required[str] type: Required[Literal["container_upload"]] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_content_block.py000066400000000000000000000040221523216435200267700ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from ..._utils import PropertyInfo from .beta_text_block import BetaTextBlock from .beta_fallback_block import BetaFallbackBlock from .beta_thinking_block import BetaThinkingBlock from .beta_tool_use_block import BetaToolUseBlock from .beta_compaction_block import BetaCompactionBlock from .beta_mcp_tool_use_block import BetaMCPToolUseBlock from .beta_mcp_tool_result_block import BetaMCPToolResultBlock from .beta_server_tool_use_block import BetaServerToolUseBlock from .beta_container_upload_block import BetaContainerUploadBlock from .beta_redacted_thinking_block import BetaRedactedThinkingBlock from .beta_advisor_tool_result_block import BetaAdvisorToolResultBlock from .beta_web_fetch_tool_result_block import BetaWebFetchToolResultBlock from .beta_web_search_tool_result_block import BetaWebSearchToolResultBlock from .beta_tool_search_tool_result_block import BetaToolSearchToolResultBlock from .beta_code_execution_tool_result_block import BetaCodeExecutionToolResultBlock from .beta_bash_code_execution_tool_result_block import BetaBashCodeExecutionToolResultBlock from .beta_text_editor_code_execution_tool_result_block import BetaTextEditorCodeExecutionToolResultBlock __all__ = ["BetaContentBlock"] BetaContentBlock: TypeAlias = Annotated[ Union[ BetaTextBlock, BetaThinkingBlock, BetaRedactedThinkingBlock, BetaToolUseBlock, BetaServerToolUseBlock, BetaWebSearchToolResultBlock, BetaWebFetchToolResultBlock, BetaAdvisorToolResultBlock, BetaCodeExecutionToolResultBlock, BetaBashCodeExecutionToolResultBlock, BetaTextEditorCodeExecutionToolResultBlock, BetaToolSearchToolResultBlock, BetaMCPToolUseBlock, BetaMCPToolResultBlock, BetaContainerUploadBlock, BetaCompactionBlock, BetaFallbackBlock, ], PropertyInfo(discriminator="type"), ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_content_block_param.py000066400000000000000000000060001523216435200301460ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from typing_extensions import TypeAlias from .beta_content_block import BetaContentBlock from .beta_text_block_param import BetaTextBlockParam from .beta_image_block_param import BetaImageBlockParam from .beta_fallback_block_param import BetaFallbackBlockParam from .beta_thinking_block_param import BetaThinkingBlockParam from .beta_tool_use_block_param import BetaToolUseBlockParam from .beta_compaction_block_param import BetaCompactionBlockParam from .beta_tool_result_block_param import BetaToolResultBlockParam from .beta_mcp_tool_use_block_param import BetaMCPToolUseBlockParam from .beta_search_result_block_param import BetaSearchResultBlockParam from .beta_server_tool_use_block_param import BetaServerToolUseBlockParam from .beta_container_upload_block_param import BetaContainerUploadBlockParam from .beta_request_document_block_param import BetaRequestDocumentBlockParam from .beta_redacted_thinking_block_param import BetaRedactedThinkingBlockParam from .beta_advisor_tool_result_block_param import BetaAdvisorToolResultBlockParam from .beta_request_tool_removal_block_param import BetaRequestToolRemovalBlockParam from .beta_request_tool_addition_block_param import BetaRequestToolAdditionBlockParam from .beta_web_fetch_tool_result_block_param import BetaWebFetchToolResultBlockParam from .beta_web_search_tool_result_block_param import BetaWebSearchToolResultBlockParam from .beta_mid_conversation_system_block_param import BetaMidConversationSystemBlockParam from .beta_request_mcp_tool_result_block_param import BetaRequestMCPToolResultBlockParam from .beta_tool_search_tool_result_block_param import BetaToolSearchToolResultBlockParam from .beta_code_execution_tool_result_block_param import BetaCodeExecutionToolResultBlockParam from .beta_bash_code_execution_tool_result_block_param import BetaBashCodeExecutionToolResultBlockParam from .beta_text_editor_code_execution_tool_result_block_param import BetaTextEditorCodeExecutionToolResultBlockParam __all__ = ["BetaContentBlockParam"] BetaContentBlockParam: TypeAlias = Union[ BetaTextBlockParam, BetaImageBlockParam, BetaRequestDocumentBlockParam, BetaSearchResultBlockParam, BetaThinkingBlockParam, BetaRedactedThinkingBlockParam, BetaToolUseBlockParam, BetaToolResultBlockParam, BetaServerToolUseBlockParam, BetaWebSearchToolResultBlockParam, BetaWebFetchToolResultBlockParam, BetaAdvisorToolResultBlockParam, BetaCodeExecutionToolResultBlockParam, BetaBashCodeExecutionToolResultBlockParam, BetaTextEditorCodeExecutionToolResultBlockParam, BetaToolSearchToolResultBlockParam, BetaMCPToolUseBlockParam, BetaRequestMCPToolResultBlockParam, BetaContainerUploadBlockParam, BetaCompactionBlockParam, BetaMidConversationSystemBlockParam, BetaRequestToolAdditionBlockParam, BetaRequestToolRemovalBlockParam, BetaFallbackBlockParam, BetaContentBlock, ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_content_block_source_content_param.py000066400000000000000000000006751523216435200332740ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from typing_extensions import TypeAlias from .beta_text_block_param import BetaTextBlockParam from .beta_image_block_param import BetaImageBlockParam __all__ = ["BetaContentBlockSourceContentParam"] BetaContentBlockSourceContentParam: TypeAlias = Union[BetaTextBlockParam, BetaImageBlockParam] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_content_block_source_param.py000066400000000000000000000010221523216435200315250ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Iterable from typing_extensions import Literal, Required, TypedDict from .beta_content_block_source_content_param import BetaContentBlockSourceContentParam __all__ = ["BetaContentBlockSourceParam"] class BetaContentBlockSourceParam(TypedDict, total=False): content: Required[Union[str, Iterable[BetaContentBlockSourceContentParam]]] type: Required[Literal["content"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_context_management_capability.py000066400000000000000000000014441523216435200322320ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from ..._models import BaseModel from .beta_capability_support import BetaCapabilitySupport __all__ = ["BetaContextManagementCapability"] class BetaContextManagementCapability(BaseModel): """Context management capability details.""" clear_thinking_20251015: Optional[BetaCapabilitySupport] = None """Indicates whether a capability is supported.""" clear_tool_uses_20250919: Optional[BetaCapabilitySupport] = None """Indicates whether a capability is supported.""" compact_20260112: Optional[BetaCapabilitySupport] = None """Indicates whether a capability is supported.""" supported: bool """Whether this capability is supported by the model.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_context_management_config_param.py000066400000000000000000000014331523216435200325340ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Iterable from typing_extensions import TypeAlias, TypedDict from .beta_compact_20260112_edit_param import BetaCompact20260112EditParam from .beta_clear_thinking_20251015_edit_param import BetaClearThinking20251015EditParam from .beta_clear_tool_uses_20250919_edit_param import BetaClearToolUses20250919EditParam __all__ = ["BetaContextManagementConfigParam", "Edit"] Edit: TypeAlias = Union[ BetaClearToolUses20250919EditParam, BetaClearThinking20251015EditParam, BetaCompact20260112EditParam ] class BetaContextManagementConfigParam(TypedDict, total=False): edits: Iterable[Edit] """List of context management edits to apply""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_context_management_response.py000066400000000000000000000014441523216435200317470ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Union from typing_extensions import Annotated, TypeAlias from ..._utils import PropertyInfo from ..._models import BaseModel from .beta_clear_thinking_20251015_edit_response import BetaClearThinking20251015EditResponse from .beta_clear_tool_uses_20250919_edit_response import BetaClearToolUses20250919EditResponse __all__ = ["BetaContextManagementResponse", "AppliedEdit"] AppliedEdit: TypeAlias = Annotated[ Union[BetaClearToolUses20250919EditResponse, BetaClearThinking20251015EditResponse], PropertyInfo(discriminator="type"), ] class BetaContextManagementResponse(BaseModel): applied_edits: List[AppliedEdit] """List of context management edits that were applied.""" beta_count_tokens_context_management_response.py000066400000000000000000000005251523216435200344620ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from ..._models import BaseModel __all__ = ["BetaCountTokensContextManagementResponse"] class BetaCountTokensContextManagementResponse(BaseModel): original_input_tokens: int """The original token count before context management was applied""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_diagnostics.py000066400000000000000000000031001523216435200264470ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union, Optional from typing_extensions import Annotated, TypeAlias from ..._utils import PropertyInfo from ..._models import BaseModel from .beta_cache_miss_unavailable import BetaCacheMissUnavailable from .beta_cache_miss_model_changed import BetaCacheMissModelChanged from .beta_cache_miss_tools_changed import BetaCacheMissToolsChanged from .beta_cache_miss_system_changed import BetaCacheMissSystemChanged from .beta_cache_miss_messages_changed import BetaCacheMissMessagesChanged from .beta_cache_miss_previous_message_not_found import BetaCacheMissPreviousMessageNotFound __all__ = ["BetaDiagnostics", "CacheMissReason"] CacheMissReason: TypeAlias = Annotated[ Union[ BetaCacheMissModelChanged, BetaCacheMissSystemChanged, BetaCacheMissToolsChanged, BetaCacheMissMessagesChanged, BetaCacheMissPreviousMessageNotFound, BetaCacheMissUnavailable, None, ], PropertyInfo(discriminator="type"), ] class BetaDiagnostics(BaseModel): """Response envelope for request-level diagnostics. Present (possibly null) whenever the caller supplied `diagnostics` on the request. """ cache_miss_reason: Optional[CacheMissReason] = None """ Explains why the prompt cache could not fully reuse the prefix from the request identified by `diagnostics.previous_message_id`. `null` means diagnosis is still pending — the response was serialized before the background comparison completed. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_diagnostics_param.py000066400000000000000000000014371523216435200276420ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import TypedDict __all__ = ["BetaDiagnosticsParam"] class BetaDiagnosticsParam(TypedDict, total=False): """Request-level diagnostics. Currently carries the previous response id for prompt-cache divergence reporting. """ previous_message_id: Optional[str] """The `id` (`msg_...`) from this client's previous /v1/messages response. The server compares that request's prompt fingerprint against this one and returns `diagnostics.cache_miss_reason` when the prompt-cache prefix could not be reused. Pass `null` on the first turn to opt in without a prior message to compare. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_direct_caller.py000066400000000000000000000004641523216435200267460ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaDirectCaller"] class BetaDirectCaller(BaseModel): """Tool invocation directly from the model.""" type: Literal["direct"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_direct_caller_param.py000066400000000000000000000005541523216435200301260ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaDirectCallerParam"] class BetaDirectCallerParam(TypedDict, total=False): """Tool invocation directly from the model.""" type: Required[Literal["direct"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_document_block.py000066400000000000000000000015021523216435200271340ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union, Optional from typing_extensions import Literal, Annotated, TypeAlias from ..._utils import PropertyInfo from ..._models import BaseModel from .beta_citation_config import BetaCitationConfig from .beta_base64_pdf_source import BetaBase64PDFSource from .beta_plain_text_source import BetaPlainTextSource __all__ = ["BetaDocumentBlock", "Source"] Source: TypeAlias = Annotated[Union[BetaBase64PDFSource, BetaPlainTextSource], PropertyInfo(discriminator="type")] class BetaDocumentBlock(BaseModel): citations: Optional[BetaCitationConfig] = None """Citation configuration for the document""" source: Source title: Optional[str] = None """The title of the document""" type: Literal["document"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_dream.py000066400000000000000000000034071523216435200252420ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from datetime import datetime from typing_extensions import Literal from ..._models import BaseModel from .beta_dream_error import BetaDreamError from .beta_dream_input import BetaDreamInput from .beta_dream_usage import BetaDreamUsage from .beta_dream_output import BetaDreamOutput from .beta_dream_status import BetaDreamStatus from .beta_dream_model_config import BetaDreamModelConfig __all__ = ["BetaDream"] class BetaDream(BaseModel): """ An asynchronous memory-consolidation job that reads a memory store plus a set of session transcripts and writes consolidated memories into a new output memory store. The Dreams API is in research preview: the request and response shapes are volatile and may change without the deprecation period that applies to generally-available endpoints. """ id: str archived_at: Optional[datetime] = None """A timestamp in RFC 3339 format""" created_at: datetime """A timestamp in RFC 3339 format""" ended_at: Optional[datetime] = None """A timestamp in RFC 3339 format""" error: Optional[BetaDreamError] = None """Failure detail for a Dream whose `status` is `failed`.""" inputs: List[BetaDreamInput] instructions: Optional[str] = None model: BetaDreamModelConfig """Model identifier and configuration applied to every pipeline stage. Same wire shape as the Agents API ModelConfig. """ outputs: List[BetaDreamOutput] session_id: Optional[str] = None status: BetaDreamStatus """Lifecycle status of a Dream.""" type: Literal["dream"] usage: BetaDreamUsage """Cumulative token usage for the dream across every pipeline stage.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_dream_error.py000066400000000000000000000004331523216435200264470ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from ..._models import BaseModel __all__ = ["BetaDreamError"] class BetaDreamError(BaseModel): """Failure detail for a Dream whose `status` is `failed`.""" message: str type: str anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_dream_input.py000066400000000000000000000007631523216435200264630ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from ..._utils import PropertyInfo from .beta_dream_sessions_input import BetaDreamSessionsInput from .beta_dream_memory_store_input import BetaDreamMemoryStoreInput __all__ = ["BetaDreamInput"] BetaDreamInput: TypeAlias = Annotated[ Union[BetaDreamMemoryStoreInput, BetaDreamSessionsInput], PropertyInfo(discriminator="type") ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_dream_input_param.py000066400000000000000000000007361523216435200276430ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from typing_extensions import TypeAlias from .beta_dream_sessions_input_param import BetaDreamSessionsInputParam from .beta_dream_memory_store_input_param import BetaDreamMemoryStoreInputParam __all__ = ["BetaDreamInputParam"] BetaDreamInputParam: TypeAlias = Union[BetaDreamMemoryStoreInputParam, BetaDreamSessionsInputParam] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_dream_memory_store_input.py000066400000000000000000000006151523216435200312630ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaDreamMemoryStoreInput"] class BetaDreamMemoryStoreInput(BaseModel): """An input memory store the dream reads from. The dream never mutates this store.""" memory_store_id: str type: Literal["memory_store"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_dream_memory_store_input_param.py000066400000000000000000000007171523216435200324460ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaDreamMemoryStoreInputParam"] class BetaDreamMemoryStoreInputParam(TypedDict, total=False): """An input memory store the dream reads from. The dream never mutates this store.""" memory_store_id: Required[str] type: Required[Literal["memory_store"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_dream_model_config.py000066400000000000000000000013531523216435200277450ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaDreamModelConfig"] class BetaDreamModelConfig(BaseModel): """Model identifier and configuration applied to every pipeline stage. Same wire shape as the Agents API ModelConfig. """ id: str """Model identifier, e.g. "claude-opus-4-7". 1-256 characters.""" speed: Optional[Literal["standard", "fast"]] = None """Inference speed mode. `fast` provides significantly faster output token generation at premium pricing. Not all models support `fast`; invalid combinations are rejected at create time. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_dream_model_config_param.py000066400000000000000000000013431523216435200311240ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaDreamModelConfigParam"] class BetaDreamModelConfigParam(TypedDict, total=False): """Model identifier and configuration applied to every pipeline stage.""" id: Required[str] """Model identifier, e.g. "claude-opus-4-7". 1-256 characters.""" speed: Optional[Literal["standard", "fast"]] """Inference speed mode. `fast` provides significantly faster output token generation at premium pricing. Not all models support `fast`; invalid combinations are rejected at create time. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_dream_output.py000066400000000000000000000005551523216435200266630ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaDreamOutput"] class BetaDreamOutput(BaseModel): """An output memory store the dream writes consolidated memories into.""" memory_store_id: str type: Literal["memory_store"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_dream_sessions_input.py000066400000000000000000000005701523216435200304050ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaDreamSessionsInput"] class BetaDreamSessionsInput(BaseModel): """Input session transcripts the dream reads.""" session_ids: List[str] type: Literal["sessions"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_dream_sessions_input_param.py000066400000000000000000000007221523216435200315640ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict from ..._types import SequenceNotStr __all__ = ["BetaDreamSessionsInputParam"] class BetaDreamSessionsInputParam(TypedDict, total=False): """Input session transcripts the dream reads.""" session_ids: Required[SequenceNotStr[str]] type: Required[Literal["sessions"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_dream_status.py000066400000000000000000000004061523216435200266410ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal, TypeAlias __all__ = ["BetaDreamStatus"] BetaDreamStatus: TypeAlias = Literal["pending", "running", "completed", "failed", "canceled"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_dream_usage.py000066400000000000000000000012171523216435200264230ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from ..._models import BaseModel __all__ = ["BetaDreamUsage"] class BetaDreamUsage(BaseModel): """Cumulative token usage for the dream across every pipeline stage.""" cache_creation_input_tokens: int """Total tokens used to create prompt-cache entries (sum of all TTL tiers).""" cache_read_input_tokens: int """Total tokens read from prompt cache.""" input_tokens: int """Total uncached input tokens consumed across every pipeline stage.""" output_tokens: int """Total output tokens generated across every pipeline stage.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_effort_capability.py000066400000000000000000000015651523216435200276430ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from ..._models import BaseModel from .beta_capability_support import BetaCapabilitySupport __all__ = ["BetaEffortCapability"] class BetaEffortCapability(BaseModel): """Effort (reasoning_effort) capability details.""" high: BetaCapabilitySupport """Whether the model supports high effort level.""" low: BetaCapabilitySupport """Whether the model supports low effort level.""" max: BetaCapabilitySupport """Whether the model supports max effort level.""" medium: BetaCapabilitySupport """Whether the model supports medium effort level.""" supported: bool """Whether this capability is supported by the model.""" xhigh: Optional[BetaCapabilitySupport] = None """Indicates whether a capability is supported.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_encrypted_code_execution_result_block.py000066400000000000000000000011561523216435200337730ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List from typing_extensions import Literal from ..._models import BaseModel from .beta_code_execution_output_block import BetaCodeExecutionOutputBlock __all__ = ["BetaEncryptedCodeExecutionResultBlock"] class BetaEncryptedCodeExecutionResultBlock(BaseModel): """Code execution result with encrypted stdout for PFC + web_search results.""" content: List[BetaCodeExecutionOutputBlock] encrypted_stdout: str return_code: int stderr: str type: Literal["encrypted_code_execution_result"] beta_encrypted_code_execution_result_block_param.py000066400000000000000000000013471523216435200350760ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Iterable from typing_extensions import Literal, Required, TypedDict from .beta_code_execution_output_block_param import BetaCodeExecutionOutputBlockParam __all__ = ["BetaEncryptedCodeExecutionResultBlockParam"] class BetaEncryptedCodeExecutionResultBlockParam(TypedDict, total=False): """Code execution result with encrypted stdout for PFC + web_search results.""" content: Required[Iterable[BetaCodeExecutionOutputBlockParam]] encrypted_stdout: Required[str] return_code: Required[int] stderr: Required[str] type: Required[Literal["encrypted_code_execution_result"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_environment.py000066400000000000000000000031261523216435200265140ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Dict, Union, Optional from typing_extensions import Literal, Annotated, TypeAlias from ..._utils import PropertyInfo from ..._models import BaseModel from .beta_cloud_config import BetaCloudConfig from .beta_self_hosted_config import BetaSelfHostedConfig __all__ = ["BetaEnvironment", "Config"] Config: TypeAlias = Annotated[Union[BetaCloudConfig, BetaSelfHostedConfig], PropertyInfo(discriminator="type")] class BetaEnvironment(BaseModel): """Unified Environment resource for both cloud and self-hosted environments.""" id: str """Environment identifier (e.g., 'env\\__...')""" archived_at: Optional[str] = None """RFC 3339 timestamp when environment was archived, or null if not archived""" config: Config """Environment configuration (either Anthropic Cloud or self-hosted)""" created_at: str """RFC 3339 timestamp when environment was created""" description: str """User-provided description for the environment""" metadata: Dict[str, str] """User-provided metadata key-value pairs""" name: str """Human-readable name for the environment""" type: Literal["environment"] """The type of object (always 'environment')""" updated_at: str """RFC 3339 timestamp when environment was last updated""" scope: Optional[Literal["organization", "account"]] = None """The visibility scope for this environment. 'organization' means visible to all accounts. 'account' means visible only to the owning account. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_environment_delete_response.py000066400000000000000000000006471523216435200317610ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaEnvironmentDeleteResponse"] class BetaEnvironmentDeleteResponse(BaseModel): """Response after deleting an environment.""" id: str """Environment identifier""" type: Literal["environment_deleted"] """The type of response""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_fallback_block.py000066400000000000000000000032271523216435200270630ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from pydantic import Field as FieldInfo from ..._models import BaseModel from .beta_fallback_info import BetaFallbackInfo from .beta_fallback_refusal_trigger import BetaFallbackRefusalTrigger __all__ = ["BetaFallbackBlock"] class BetaFallbackBlock(BaseModel): """Marks the point in `content` where one model's output gives way to the next. One block appears per hop where a preceding model actually ran this turn and declined. A turn where no preceding model ran and declined has no such boundary and carries no block — the signal for whether a fallback model served the response is the presence of a `fallback_message` entry in `usage.iterations`, not this block. The block is treated like a server-tool content block for streaming: it arrives via the standard `content_block_start` / `content_block_stop` pair and carries no deltas. """ from_: BetaFallbackInfo = FieldInfo(alias="from") """The model whose output ends at this point — the model that declined at this hop. When the declining hop is the requested model, its `model` echoes the top-level `model` string the caller sent (alias or canonical); when the declining hop is a fallback model, its `model` is that model's canonical id. """ to: BetaFallbackInfo """The fallback model producing the content that follows this block. Its `model` is always the canonical id. """ trigger: BetaFallbackRefusalTrigger """What caused the `from` model to hand over at this hop.""" type: Literal["fallback"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_fallback_block_param.py000066400000000000000000000031051523216435200302360ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict from .beta_fallback_info_param import BetaFallbackInfoParam __all__ = ["BetaFallbackBlockParam"] _BetaFallbackBlockParamReservedKeywords = TypedDict( "_BetaFallbackBlockParamReservedKeywords", { "from": BetaFallbackInfoParam, }, total=False, ) class BetaFallbackBlockParam(_BetaFallbackBlockParamReservedKeywords, total=False): """A `fallback` block echoed back from a prior response. Accepted in `messages[].content` and not rendered into the prompt; not validated against the request's `fallbacks` chain or top-level `model`. Echo the assistant turn back verbatim, including this block in its original position. The block marks the boundary between content produced before and after a fallback hop, and the server relies on that boundary to validate the turn: when thinking runs flank the boundary, omitting the block merges them into one span the server cannot validate (the request is rejected), and moving it into the middle of a single run is likewise rejected; between non-thinking blocks the block's placement has no validation effect. """ to: Required[BetaFallbackInfoParam] """Identifies one hop of a fallback transition.""" type: Required[Literal["fallback"]] trigger: object """The response block's `trigger`, echoed verbatim. Accepted and ignored by the server; any object or `null` is allowed. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_fallback_credit_not_applied.py000066400000000000000000000026531523216435200316230ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaFallbackCreditNotApplied"] class BetaFallbackCreditNotApplied(BaseModel): """No reprice was applied; ``reason`` says why.""" reason: Literal[ "body_mismatch", "continuation_excluded", "continuation_only", "expired", "invalid_target_model", "not_enabled", "reprice_unavailable", "temporarily_unavailable", "variant_fields_present", "wrong_organization", "wrong_platform", "wrong_workspace", ] """Why the reprice was not applied. A closed enum; additions to the redemption-check vocabulary arrive as deliberate schema updates. """ type: Literal["not_applied"] remove_to_redeem: Optional[List[str]] = None """Request fields to remove before retrying, so the retry can redeem this token. Present exactly when `reason` is `variant_fields_present` — never null, never an empty array; absent otherwise. Fields are named only from your own request, and only after the sealed variant hash matched. A served best-effort retry has already been billed at normal price; nothing redeems retroactively, but a corrected re-send inside the token's five-minute window can still redeem. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_fallback_credit_redeemed.py000066400000000000000000000006321523216435200310720ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaFallbackCreditRedeemed"] class BetaFallbackCreditRedeemed(BaseModel): """ The reprice was applied: the retry is billed as if the conversation had been on the retry model all along. """ type: Literal["redeemed"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_fallback_credit_token_param.py000066400000000000000000000025531523216435200316240ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaFallbackCreditTokenParam"] class BetaFallbackCreditTokenParam(TypedDict, total=False): """Object form of ``fallback_credit_token``: the token plus a redemption mode. Requires ``anthropic-beta: fallback-credit-2026-07-01``; without that header the field accepts the bare string only. The bare string and the mode-less object are equivalent (both select ``strict``), so wrapping an existing token changes nothing by itself. """ token: Required[str] """ The opaque `fallback_credit_token` from a prior refusal's `stop_details` — the same string the bare-string form carries. """ mode: Literal["strict", "best_effort"] """How a failing token affects the retry. `strict` (the default, and the bare-string behavior): a failing redemption is a 400 and the retry is not served. `best_effort`: the retry is served either way — a token-layer failure no longer rejects the request; the retry proceeds at normal price and the outcome is reported on the response's `usage.fallback_credit`. Two failures stay hard in both modes: a malformed token, and combining `fallback_credit_token` with `fallbacks`. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_fallback_credit_usage.py000066400000000000000000000021021523216435200304160ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from ..._utils import PropertyInfo from ..._models import BaseModel from .beta_fallback_credit_redeemed import BetaFallbackCreditRedeemed from .beta_fallback_credit_not_applied import BetaFallbackCreditNotApplied __all__ = ["BetaFallbackCreditUsage", "Status"] Status: TypeAlias = Annotated[ Union[BetaFallbackCreditRedeemed, BetaFallbackCreditNotApplied], PropertyInfo(discriminator="type") ] class BetaFallbackCreditUsage(BaseModel): """Outcome of the ``fallback_credit_token`` presented on this request.""" status: Status """Whether the fallback-credit reprice was applied to this response's billing. A union discriminated on `type`. `redeemed`: the retry is billed as if the conversation had been on the retry model all along — including when the resulting shift is zero because there was nothing to move. `not_applied`: no reprice was applied; the arm's `reason` says why. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_fallback_info.py000066400000000000000000000007071523216435200267240ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from ..model import Model from ..._models import BaseModel __all__ = ["BetaFallbackInfo"] class BetaFallbackInfo(BaseModel): """Identifies one hop of a fallback transition.""" model: Model """The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_fallback_info_param.py000066400000000000000000000010561523216435200301020ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Required, TypedDict from ..model_param import ModelParam __all__ = ["BetaFallbackInfoParam"] class BetaFallbackInfoParam(TypedDict, total=False): """Identifies one hop of a fallback transition.""" model: Required[ModelParam] """The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_fallback_message_iteration_usage.py000066400000000000000000000026771523216435200326670ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ..model import Model from ..._models import BaseModel from .beta_cache_creation import BetaCacheCreation __all__ = ["BetaFallbackMessageIterationUsage"] class BetaFallbackMessageIterationUsage(BaseModel): """Token usage for the fallback-model attempt of a server-side fallback request. Produced in place of a `message` entry for whichever hop served the response. A declined hop produces the existing `message` entry. Whether a fallback model served the response is signalled by the presence of this entry in `usage.iterations`. """ cache_creation: Optional[BetaCacheCreation] = None """Breakdown of cached tokens by TTL""" cache_creation_input_tokens: int """The number of input tokens used to create the cache entry.""" cache_read_input_tokens: int """The number of input tokens read from the cache.""" input_tokens: int """The number of input tokens which were used.""" model: Model """The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. """ output_tokens: int """The number of output tokens which were used.""" type: Literal["fallback_message"] """Usage for the fallback-model attempt that served the response""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_fallback_param.py000066400000000000000000000033441523216435200270710ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..model_param import ModelParam from .beta_output_config_param import BetaOutputConfigParam from .beta_thinking_config_enabled_param import BetaThinkingConfigEnabledParam from .beta_thinking_config_adaptive_param import BetaThinkingConfigAdaptiveParam from .beta_thinking_config_disabled_param import BetaThinkingConfigDisabledParam __all__ = ["BetaFallbackParam", "Thinking"] Thinking: TypeAlias = Union[ BetaThinkingConfigEnabledParam, BetaThinkingConfigDisabledParam, BetaThinkingConfigAdaptiveParam ] class BetaFallbackParam(TypedDict, total=False, extra_items=object): # type: ignore[call-arg] """One entry in the `fallbacks` chain on a `/v1/messages` request. `model` is required. The override fields (`max_tokens`, `thinking`, `output_config`, and `speed`) set the corresponding parameter for this attempt only and are validated as if the request were made to `model`. Any other key is rejected at parse time. """ model: Required[ModelParam] """The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. """ max_tokens: Optional[int] output_config: Optional[BetaOutputConfigParam] speed: Optional[Literal["standard", "fast"]] """Inference speed mode. `fast` provides significantly faster output token generation at premium pricing. Not all models support `fast`; invalid combinations are rejected at create time. """ thinking: Optional[Thinking] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_fallback_refusal_trigger.py000066400000000000000000000030241523216435200311500ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaFallbackRefusalTrigger"] class BetaFallbackRefusalTrigger(BaseModel): """The `from` model declined for policy reasons.""" category: Optional[Literal["cyber", "bio", "frontier_llm", "reasoning_extraction", "general_harms"]] = None """The policy category that triggered a refusal. - `cyber` - The request could enable cyber harm, such as malware or exploit development. Benign cybersecurity work can also trigger this category. - `bio` - The request could enable biological harm, such as dangerous lab methods. Beneficial life sciences work can also trigger this category. - `frontier_llm` - The request could assist the development of competing AI models, which is restricted under [Anthropic's commercial terms](https://www.anthropic.com/legal/commercial-terms). Benign machine learning work can also trigger this category. - `reasoning_extraction` - The request asks the model to reproduce its internal reasoning in the response text. To get reasoning in a structured form instead, use [adaptive thinking](https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking). - `general_harms` - The request could be related to an area that was determined as harmful. Benign work might sometimes trigger this category. """ type: Literal["refusal"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_fallbacks_param.py000066400000000000000000000005751523216435200272570ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Iterable from typing_extensions import Literal, TypeAlias from .beta_fallback_param import BetaFallbackParam __all__ = ["BetaFallbacksParam"] BetaFallbacksParam: TypeAlias = Union[Iterable[BetaFallbackParam], Literal["default"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_file_document_source_param.py000066400000000000000000000005361523216435200315270ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaFileDocumentSourceParam"] class BetaFileDocumentSourceParam(TypedDict, total=False): file_id: Required[str] type: Required[Literal["file"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_file_image_source_param.py000066400000000000000000000005301523216435200307650ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaFileImageSourceParam"] class BetaFileImageSourceParam(TypedDict, total=False): file_id: Required[str] type: Required[Literal["file"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_file_scope.py000066400000000000000000000005721523216435200262620ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaFileScope"] class BetaFileScope(BaseModel): id: str """The ID of the scoping resource (e.g., the session ID).""" type: Literal["session"] """The type of scope (e.g., `"session"`).""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_image_block_param.py000066400000000000000000000016161523216435200275660ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .beta_url_image_source_param import BetaURLImageSourceParam from .beta_file_image_source_param import BetaFileImageSourceParam from .beta_base64_image_source_param import BetaBase64ImageSourceParam from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaImageBlockParam", "Source"] Source: TypeAlias = Union[BetaBase64ImageSourceParam, BetaURLImageSourceParam, BetaFileImageSourceParam] class BetaImageBlockParam(TypedDict, total=False): source: Required[Source] type: Required[Literal["image"]] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_input_json_delta.py000066400000000000000000000004451523216435200275120ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaInputJSONDelta"] class BetaInputJSONDelta(BaseModel): partial_json: str type: Literal["input_json_delta"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_input_tokens_clear_at_least_param.py000066400000000000000000000005561523216435200331000ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaInputTokensClearAtLeastParam"] class BetaInputTokensClearAtLeastParam(TypedDict, total=False): type: Required[Literal["input_tokens"]] value: Required[int] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_input_tokens_trigger_param.py000066400000000000000000000005441523216435200315760ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaInputTokensTriggerParam"] class BetaInputTokensTriggerParam(TypedDict, total=False): type: Required[Literal["input_tokens"]] value: Required[int] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_iterations_usage.py000066400000000000000000000016141523216435200275150ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Union from typing_extensions import Annotated, TypeAlias from ..._utils import PropertyInfo from .beta_message_iteration_usage import BetaMessageIterationUsage from .beta_compaction_iteration_usage import BetaCompactionIterationUsage from .beta_advisor_message_iteration_usage import BetaAdvisorMessageIterationUsage from .beta_fallback_message_iteration_usage import BetaFallbackMessageIterationUsage __all__ = ["BetaIterationsUsage", "BetaIterationsUsageItem"] BetaIterationsUsageItem: TypeAlias = Annotated[ Union[ BetaMessageIterationUsage, BetaCompactionIterationUsage, BetaAdvisorMessageIterationUsage, BetaFallbackMessageIterationUsage, ], PropertyInfo(discriminator="type"), ] BetaIterationsUsage: TypeAlias = List[BetaIterationsUsageItem] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_json_output_format_param.py000066400000000000000000000006561523216435200312760ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaJSONOutputFormatParam"] class BetaJSONOutputFormatParam(TypedDict, total=False): schema: Required[Dict[str, object]] """The JSON schema of the format""" type: Required[Literal["json_schema"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_limited_network.py000066400000000000000000000014241523216435200273470ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaLimitedNetwork"] class BetaLimitedNetwork(BaseModel): """Limited network access.""" allow_mcp_servers: bool """ Permits outbound access to MCP server endpoints configured on the agent, beyond those listed in the `allowed_hosts` array. """ allow_package_managers: bool """ Permits outbound access to public package registries (PyPI, npm, etc.) beyond those listed in the `allowed_hosts` array. """ allowed_hosts: List[str] """Specifies domains the container can reach.""" type: Literal["limited"] """Network policy type""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_limited_network_params.py000066400000000000000000000020571523216435200307150ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict from ..._types import SequenceNotStr __all__ = ["BetaLimitedNetworkParams"] class BetaLimitedNetworkParams(TypedDict, total=False): """Limited network request params. Fields default to null; on update, omitted fields preserve the existing value. """ type: Required[Literal["limited"]] """Network policy type""" allow_mcp_servers: Optional[bool] """ Permits outbound access to MCP server endpoints configured on the agent, beyond those listed in the `allowed_hosts` array. Defaults to `false`. """ allow_package_managers: Optional[bool] """ Permits outbound access to public package registries (PyPI, npm, etc.) beyond those listed in the `allowed_hosts` array. Defaults to `false`. """ allowed_hosts: Optional[SequenceNotStr[str]] """Specifies domains the container can reach.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_agent.py000066400000000000000000000042551523216435200302670ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Dict, List, Union, Optional from datetime import datetime from typing_extensions import Literal, Annotated, TypeAlias from ..._utils import PropertyInfo from ..._models import BaseModel from .beta_managed_agents_multiagent import BetaManagedAgentsMultiagent from .beta_managed_agents_custom_tool import BetaManagedAgentsCustomTool from .beta_managed_agents_mcp_toolset import BetaManagedAgentsMCPToolset from .beta_managed_agents_custom_skill import BetaManagedAgentsCustomSkill from .beta_managed_agents_model_config import BetaManagedAgentsModelConfig from .beta_managed_agents_anthropic_skill import BetaManagedAgentsAnthropicSkill from .beta_managed_agents_agent_toolset20260401 import BetaManagedAgentsAgentToolset20260401 from .beta_managed_agents_mcp_server_url_definition import BetaManagedAgentsMCPServerURLDefinition __all__ = ["BetaManagedAgentsAgent", "Skill", "Tool"] Skill: TypeAlias = Annotated[ Union[BetaManagedAgentsAnthropicSkill, BetaManagedAgentsCustomSkill], PropertyInfo(discriminator="type") ] Tool: TypeAlias = Annotated[ Union[BetaManagedAgentsAgentToolset20260401, BetaManagedAgentsMCPToolset, BetaManagedAgentsCustomTool], PropertyInfo(discriminator="type"), ] class BetaManagedAgentsAgent(BaseModel): """A Managed Agents `agent`.""" id: str archived_at: Optional[datetime] = None """A timestamp in RFC 3339 format""" created_at: datetime """A timestamp in RFC 3339 format""" description: Optional[str] = None mcp_servers: List[BetaManagedAgentsMCPServerURLDefinition] metadata: Dict[str, str] model: BetaManagedAgentsModelConfig """Model identifier and configuration.""" multiagent: Optional[BetaManagedAgentsMultiagent] = None """Resolved coordinator topology with a concrete agent roster.""" name: str skills: List[Skill] system: Optional[str] = None tools: List[Tool] type: Literal["agent"] updated_at: datetime """A timestamp in RFC 3339 format""" version: int """The agent's current version. Starts at 1 and increments when the agent is modified. """ beta_managed_agents_agent_archived_deployment_paused_reason_error.py000066400000000000000000000006201523216435200404260ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsAgentArchivedDeploymentPausedReasonError"] class BetaManagedAgentsAgentArchivedDeploymentPausedReasonError(BaseModel): """The deployment's agent was archived.""" type: Literal["agent_archived_error"] beta_managed_agents_agent_archived_run_error.py000066400000000000000000000006501523216435200341450ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsAgentArchivedRunError"] class BetaManagedAgentsAgentArchivedRunError(BaseModel): """The deployment's agent was archived.""" message: str """Human-readable error description.""" type: Literal["agent_archived_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_agent_message_preview.py000066400000000000000000000007121523216435200335260ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsAgentMessagePreview"] class BetaManagedAgentsAgentMessagePreview(BaseModel): id: str """The id the buffered agent.message will carry if it is emitted. Matches the event_id on this preview's event_delta events. """ type: Literal["agent.message"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_agent_params.py000066400000000000000000000012251523216435200316240ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsAgentParams"] class BetaManagedAgentsAgentParams(TypedDict, total=False): """Specification for an Agent. Provide a specific `version` or use the short-form `agent="agent_id"` for the most recent version """ id: Required[str] """The `agent` ID.""" type: Required[Literal["agent"]] version: int """The specific `agent` version to use. Omit to use the latest version. Must be at least 1 if specified. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_agent_reference.py000066400000000000000000000005731523216435200323040ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsAgentReference"] class BetaManagedAgentsAgentReference(BaseModel): """A resolved agent reference with a concrete version.""" id: str type: Literal["agent"] version: int anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_agent_thinking_preview.py000066400000000000000000000007001523216435200337120ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsAgentThinkingPreview"] class BetaManagedAgentsAgentThinkingPreview(BaseModel): id: str """The id the buffered agent.thinking will carry if it is emitted. Start-only — no event_delta events follow. """ type: Literal["agent.thinking"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_agent_tool_config.py000066400000000000000000000017361523216435200326520ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Literal, Annotated, TypeAlias from ..._utils import PropertyInfo from ..._models import BaseModel from .beta_managed_agents_always_ask_policy import BetaManagedAgentsAlwaysAskPolicy from .beta_managed_agents_always_allow_policy import BetaManagedAgentsAlwaysAllowPolicy __all__ = ["BetaManagedAgentsAgentToolConfig", "PermissionPolicy"] PermissionPolicy: TypeAlias = Annotated[ Union[BetaManagedAgentsAlwaysAllowPolicy, BetaManagedAgentsAlwaysAskPolicy], PropertyInfo(discriminator="type") ] class BetaManagedAgentsAgentToolConfig(BaseModel): """Configuration for a specific agent tool.""" enabled: bool name: Literal["bash", "edit", "read", "write", "glob", "grep", "web_fetch", "web_search"] """Built-in agent tool identifier.""" permission_policy: PermissionPolicy """Permission policy for tool execution.""" beta_managed_agents_agent_tool_config_params.py000066400000000000000000000021701523216435200341270ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .beta_managed_agents_always_ask_policy_param import BetaManagedAgentsAlwaysAskPolicyParam from .beta_managed_agents_always_allow_policy_param import BetaManagedAgentsAlwaysAllowPolicyParam __all__ = ["BetaManagedAgentsAgentToolConfigParams", "PermissionPolicy"] PermissionPolicy: TypeAlias = Union[BetaManagedAgentsAlwaysAllowPolicyParam, BetaManagedAgentsAlwaysAskPolicyParam] class BetaManagedAgentsAgentToolConfigParams(TypedDict, total=False): """Configuration override for a specific tool within a toolset.""" name: Required[Literal["bash", "edit", "read", "write", "glob", "grep", "web_fetch", "web_search"]] """Built-in agent tool identifier.""" enabled: Optional[bool] """Whether this tool is enabled and available to Claude. Overrides the default_config setting. """ permission_policy: Optional[PermissionPolicy] """Permission policy for tool execution.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_agent_toolset20260401.py000066400000000000000000000012761523216435200326570ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List from typing_extensions import Literal from ..._models import BaseModel from .beta_managed_agents_agent_tool_config import BetaManagedAgentsAgentToolConfig from .beta_managed_agents_agent_toolset_default_config import BetaManagedAgentsAgentToolsetDefaultConfig __all__ = ["BetaManagedAgentsAgentToolset20260401"] class BetaManagedAgentsAgentToolset20260401(BaseModel): configs: List[BetaManagedAgentsAgentToolConfig] default_config: BetaManagedAgentsAgentToolsetDefaultConfig """Resolved default configuration for agent tools.""" type: Literal["agent_toolset_20260401"] beta_managed_agents_agent_toolset20260401_bash_input.py000066400000000000000000000017411523216435200350110ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from ..._models import BaseModel __all__ = ["BetaManagedAgentsAgentToolset20260401BashInput"] class BetaManagedAgentsAgentToolset20260401BashInput(BaseModel): """Input payload for the `bash` tool of the `agent_toolset_20260401` toolset. All fields are optional; a normal invocation supplies `command`, while `restart=true` (with no `command`) reboots the runner-side bash session. """ command: Optional[str] = None """Shell command to execute. Omit only when `restart` is true.""" restart: Optional[bool] = None """When true, restart the persistent bash session instead of running a command. Subsequent calls without `restart` will run against the fresh session. """ timeout_ms: Optional[int] = None """Per-call timeout in milliseconds. Defaults to the runner-wide tool timeout when omitted or zero. """ beta_managed_agents_agent_toolset20260401_edit_input.py000066400000000000000000000013671523216435200350250ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from ..._models import BaseModel __all__ = ["BetaManagedAgentsAgentToolset20260401EditInput"] class BetaManagedAgentsAgentToolset20260401EditInput(BaseModel): """Input payload for the `edit` tool. Performs a string replacement in the named file; by default `old_string` must occur exactly once. """ file_path: str """Path of the file to edit.""" new_string: str """Replacement text.""" old_string: str """Substring to find and replace.""" replace_all: Optional[bool] = None """ When true, replace every occurrence of `old_string` instead of requiring a unique match. """ beta_managed_agents_agent_toolset20260401_glob_input.py000066400000000000000000000013001523216435200350060ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from ..._models import BaseModel __all__ = ["BetaManagedAgentsAgentToolset20260401GlobInput"] class BetaManagedAgentsAgentToolset20260401GlobInput(BaseModel): """Input payload for the `glob` tool. Returns paths matching a doublestar glob pattern, newest first. """ pattern: str """Doublestar glob pattern (e.g. `**/*.go`). Absolute patterns are only permitted when the runner is configured to allow them. """ path: Optional[str] = None """Optional directory root to search under. Defaults to the runner's working directory. """ beta_managed_agents_agent_toolset20260401_grep_input.py000066400000000000000000000011431523216435200350250ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from ..._models import BaseModel __all__ = ["BetaManagedAgentsAgentToolset20260401GrepInput"] class BetaManagedAgentsAgentToolset20260401GrepInput(BaseModel): """Input payload for the `grep` tool. Searches file contents for a regular expression, returning matching lines. """ pattern: str """Regular expression to search for.""" path: Optional[str] = None """Optional directory root to search under. Defaults to the runner's working directory. """ beta_managed_agents_agent_toolset20260401_params.py000066400000000000000000000017541523216435200341440ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Iterable, Optional from typing_extensions import Literal, Required, TypedDict from .beta_managed_agents_agent_tool_config_params import BetaManagedAgentsAgentToolConfigParams from .beta_managed_agents_agent_toolset_default_config_params import BetaManagedAgentsAgentToolsetDefaultConfigParams __all__ = ["BetaManagedAgentsAgentToolset20260401Params"] class BetaManagedAgentsAgentToolset20260401Params(TypedDict, total=False): """Configuration for built-in agent tools. Use this to enable or disable groups of tools available to the agent. """ type: Required[Literal["agent_toolset_20260401"]] configs: Iterable[BetaManagedAgentsAgentToolConfigParams] """Per-tool configuration overrides.""" default_config: Optional[BetaManagedAgentsAgentToolsetDefaultConfigParams] """Default configuration for all tools in a toolset.""" beta_managed_agents_agent_toolset20260401_read_input.py000066400000000000000000000013301523216435200350010ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from ..._models import BaseModel __all__ = ["BetaManagedAgentsAgentToolset20260401ReadInput"] class BetaManagedAgentsAgentToolset20260401ReadInput(BaseModel): """Input payload for the `read` tool. Reads file contents relative to the runner's working directory (or absolute when the runner permits). """ file_path: str """Path of the file to read.""" view_range: Optional[List[int]] = None """Optional `[start_line, end_line]` 1-indexed inclusive range. When omitted the entire file is returned. `end_line` of 0 or negative means "to end of file". """ beta_managed_agents_agent_toolset20260401_write_input.py000066400000000000000000000007301523216435200352230ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from ..._models import BaseModel __all__ = ["BetaManagedAgentsAgentToolset20260401WriteInput"] class BetaManagedAgentsAgentToolset20260401WriteInput(BaseModel): """Input payload for the `write` tool. Writes (overwriting) the entire file contents. """ content: str """Full file contents to write.""" file_path: str """Path of the file to write.""" beta_managed_agents_agent_toolset_default_config.py000066400000000000000000000015471523216435200350130ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from ..._utils import PropertyInfo from ..._models import BaseModel from .beta_managed_agents_always_ask_policy import BetaManagedAgentsAlwaysAskPolicy from .beta_managed_agents_always_allow_policy import BetaManagedAgentsAlwaysAllowPolicy __all__ = ["BetaManagedAgentsAgentToolsetDefaultConfig", "PermissionPolicy"] PermissionPolicy: TypeAlias = Annotated[ Union[BetaManagedAgentsAlwaysAllowPolicy, BetaManagedAgentsAlwaysAskPolicy], PropertyInfo(discriminator="type") ] class BetaManagedAgentsAgentToolsetDefaultConfig(BaseModel): """Resolved default configuration for agent tools.""" enabled: bool permission_policy: PermissionPolicy """Permission policy for tool execution.""" beta_managed_agents_agent_toolset_default_config_params.py000066400000000000000000000017401523216435200363510ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from typing_extensions import TypeAlias, TypedDict from .beta_managed_agents_always_ask_policy_param import BetaManagedAgentsAlwaysAskPolicyParam from .beta_managed_agents_always_allow_policy_param import BetaManagedAgentsAlwaysAllowPolicyParam __all__ = ["BetaManagedAgentsAgentToolsetDefaultConfigParams", "PermissionPolicy"] PermissionPolicy: TypeAlias = Union[BetaManagedAgentsAlwaysAllowPolicyParam, BetaManagedAgentsAlwaysAskPolicyParam] class BetaManagedAgentsAgentToolsetDefaultConfigParams(TypedDict, total=False): """Default configuration for all tools in a toolset.""" enabled: Optional[bool] """Whether tools are enabled and available to Claude by default. Defaults to true if not specified. """ permission_policy: Optional[PermissionPolicy] """Permission policy for tool execution.""" beta_managed_agents_agent_with_overrides_params.py000066400000000000000000000051151523216435200346640ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .beta_managed_agents_model_param import BetaManagedAgentsModelParam from .beta_managed_agents_skill_params import BetaManagedAgentsSkillParams from .beta_managed_agents_custom_tool_params import BetaManagedAgentsCustomToolParams from .beta_managed_agents_mcp_toolset_params import BetaManagedAgentsMCPToolsetParams from .beta_managed_agents_model_config_params import BetaManagedAgentsModelConfigParams from .beta_managed_agents_url_mcp_server_params import BetaManagedAgentsURLMCPServerParams from .beta_managed_agents_agent_toolset20260401_params import BetaManagedAgentsAgentToolset20260401Params __all__ = ["BetaManagedAgentsAgentWithOverridesParams", "Model", "Tool"] Model: TypeAlias = Union[BetaManagedAgentsModelParam, BetaManagedAgentsModelConfigParams] Tool: TypeAlias = Union[ BetaManagedAgentsAgentToolset20260401Params, BetaManagedAgentsMCPToolsetParams, BetaManagedAgentsCustomToolParams ] class BetaManagedAgentsAgentWithOverridesParams(TypedDict, total=False): """Reference to an `agent` plus optional configuration overrides. Each provided field replaces the agent's value for the caller's use; the agent resource is unchanged. """ id: Required[str] """The `agent` ID.""" type: Required[Literal["agent_with_overrides"]] mcp_servers: Iterable[BetaManagedAgentsURLMCPServerParams] """Replacement MCP server list. Full replacement: the provided array becomes the MCP servers. Send an empty array to clear; omit to preserve the agent's servers. """ model: Model """Replacement model. Accepts the model string, e.g. `claude-opus-4-6`, or a `model_config` object. Omit to use the agent's model. """ skills: Iterable[BetaManagedAgentsSkillParams] """Replacement skill list. Full replacement: the provided array becomes the skills. Send an empty array to clear; omit to preserve the agent's skills. """ system: Optional[str] """Replacement system prompt. Up to 100,000 characters. Set to null to clear the agent's system prompt; omit to preserve it. """ tools: Iterable[Tool] """Replacement tool list. Full replacement: the provided array becomes the tool configuration. Send an empty array to clear; omit to preserve the agent's tools. """ version: int """The specific `agent` version to use. Omit to use the latest version.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_always_allow_policy.py000066400000000000000000000005661523216435200332470ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsAlwaysAllowPolicy"] class BetaManagedAgentsAlwaysAllowPolicy(BaseModel): """Tool calls are automatically approved without user confirmation.""" type: Literal["always_allow"] beta_managed_agents_always_allow_policy_param.py000066400000000000000000000006561523216435200343500ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsAlwaysAllowPolicyParam"] class BetaManagedAgentsAlwaysAllowPolicyParam(TypedDict, total=False): """Tool calls are automatically approved without user confirmation.""" type: Required[Literal["always_allow"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_always_ask_policy.py000066400000000000000000000005461523216435200327050ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsAlwaysAskPolicy"] class BetaManagedAgentsAlwaysAskPolicy(BaseModel): """Tool calls require user confirmation before execution.""" type: Literal["always_ask"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_always_ask_policy_param.py000066400000000000000000000006361523216435200340650ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsAlwaysAskPolicyParam"] class BetaManagedAgentsAlwaysAskPolicyParam(TypedDict, total=False): """Tool calls require user confirmation before execution.""" type: Required[Literal["always_ask"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_anthropic_skill.py000066400000000000000000000005651523216435200323560ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsAnthropicSkill"] class BetaManagedAgentsAnthropicSkill(BaseModel): """A resolved Anthropic-managed skill.""" skill_id: str type: Literal["anthropic"] version: str anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_anthropic_skill_params.py000066400000000000000000000011141523216435200337100ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsAnthropicSkillParams"] class BetaManagedAgentsAnthropicSkillParams(TypedDict, total=False): """An Anthropic-managed skill.""" skill_id: Required[str] """Identifier of the Anthropic skill (e.g., "xlsx").""" type: Required[Literal["anthropic"]] version: Optional[str] """Version to pin. Defaults to latest if omitted.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_branch_checkout.py000066400000000000000000000005211523216435200323030ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsBranchCheckout"] class BetaManagedAgentsBranchCheckout(BaseModel): name: str """Branch name to check out.""" type: Literal["branch"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_branch_checkout_param.py000066400000000000000000000006231523216435200334660ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsBranchCheckoutParam"] class BetaManagedAgentsBranchCheckoutParam(TypedDict, total=False): name: Required[str] """Branch name to check out.""" type: Required[Literal["branch"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_cache_creation_usage.py000066400000000000000000000010671523216435200333020ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from ..._models import BaseModel __all__ = ["BetaManagedAgentsCacheCreationUsage"] class BetaManagedAgentsCacheCreationUsage(BaseModel): """Prompt-cache creation token usage broken down by cache lifetime.""" ephemeral_1h_input_tokens: Optional[int] = None """Tokens used to create 1-hour ephemeral cache entries.""" ephemeral_5m_input_tokens: Optional[int] = None """Tokens used to create 5-minute ephemeral cache entries.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_commit_checkout.py000066400000000000000000000005241523216435200323410ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsCommitCheckout"] class BetaManagedAgentsCommitCheckout(BaseModel): sha: str """Full commit SHA to check out.""" type: Literal["commit"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_commit_checkout_param.py000066400000000000000000000006261523216435200335240ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsCommitCheckoutParam"] class BetaManagedAgentsCommitCheckoutParam(TypedDict, total=False): sha: Required[str] """Full commit SHA to check out.""" type: Required[Literal["commit"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_custom_skill.py000066400000000000000000000005561523216435200317010ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsCustomSkill"] class BetaManagedAgentsCustomSkill(BaseModel): """A resolved user-created custom skill.""" skill_id: str type: Literal["custom"] version: str anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_custom_skill_params.py000066400000000000000000000011121523216435200332310ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsCustomSkillParams"] class BetaManagedAgentsCustomSkillParams(TypedDict, total=False): """A user-created custom skill.""" skill_id: Required[str] """Tagged ID of the custom skill (e.g., "skill_01XJ5...").""" type: Required[Literal["custom"]] version: Optional[str] """Version to pin. Defaults to latest if omitted.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_custom_tool.py000066400000000000000000000011051523216435200315270ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel from .beta_managed_agents_custom_tool_input_schema import BetaManagedAgentsCustomToolInputSchema __all__ = ["BetaManagedAgentsCustomTool"] class BetaManagedAgentsCustomTool(BaseModel): """A custom tool as returned in API responses.""" description: str input_schema: BetaManagedAgentsCustomToolInputSchema """JSON Schema for custom tool input parameters.""" name: str type: Literal["custom"] beta_managed_agents_custom_tool_input_schema.py000066400000000000000000000022151523216435200342120ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import TYPE_CHECKING, Dict, List, Optional from typing_extensions import Literal from pydantic import Field as FieldInfo from ..._models import BaseModel __all__ = ["BetaManagedAgentsCustomToolInputSchema"] class BetaManagedAgentsCustomToolInputSchema(BaseModel): """JSON Schema for custom tool input parameters.""" type: Literal["object"] properties: Optional[Dict[str, object]] = None required: Optional[List[str]] = None if TYPE_CHECKING: # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a # value to this field, so for compatibility we avoid doing it at runtime. __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride] # Stub to indicate that arbitrary properties are accepted. # To access properties that are not valid identifiers you can use `getattr`, e.g. # `getattr(obj, '$type')` def __getattr__(self, attr: str) -> object: ... else: __pydantic_extra__: Dict[str, object] beta_managed_agents_custom_tool_input_schema_param.py000066400000000000000000000011551523216435200353740ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, Optional from typing_extensions import Literal, Required, TypedDict from ..._types import SequenceNotStr __all__ = ["BetaManagedAgentsCustomToolInputSchemaParam"] class BetaManagedAgentsCustomToolInputSchemaParam(TypedDict, total=False, extra_items=object): # type: ignore[call-arg] """JSON Schema for custom tool input parameters.""" type: Required[Literal["object"]] properties: Optional[Dict[str, object]] required: Optional[SequenceNotStr[str]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_custom_tool_params.py000066400000000000000000000022031523216435200330720ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict from .beta_managed_agents_custom_tool_input_schema_param import BetaManagedAgentsCustomToolInputSchemaParam __all__ = ["BetaManagedAgentsCustomToolParams"] class BetaManagedAgentsCustomToolParams(TypedDict, total=False): """A custom tool that is executed by the API client rather than the agent. When the agent calls this tool, an `agent.custom_tool_use` event is emitted and the session goes idle, waiting for the client to provide the result via a `user.custom_tool_result` event. """ description: Required[str] """ Description of what the tool does, shown to the agent to help it decide when to use the tool. 1-4096 characters. """ input_schema: Required[BetaManagedAgentsCustomToolInputSchemaParam] """JSON Schema for custom tool input parameters.""" name: Required[str] """Unique name for the tool. 1-128 characters; letters, digits, underscores, and hyphens. """ type: Required[Literal["custom"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_deleted_memory_store.py000066400000000000000000000010251523216435200333730ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsDeletedMemoryStore"] class BetaManagedAgentsDeletedMemoryStore(BaseModel): """Confirmation that a `memory_store` was deleted.""" id: str """ID of the deleted memory store (a `memstore_...` identifier). The store and all its memories and versions are no longer retrievable. """ type: Literal["memory_store_deleted"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_deleted_session.py000066400000000000000000000005731523216435200323410ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsDeletedSession"] class BetaManagedAgentsDeletedSession(BaseModel): """Confirmation that a `session` has been permanently deleted.""" id: str type: Literal["session_deleted"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_deleted_vault.py000066400000000000000000000006141523216435200320050ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsDeletedVault"] class BetaManagedAgentsDeletedVault(BaseModel): """Confirmation of a deleted vault.""" id: str """Unique identifier of the deleted vault.""" type: Literal["vault_deleted"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_delta_content.py000066400000000000000000000012661523216435200320130ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ..._models import BaseModel from .sessions.beta_managed_agents_text_block import BetaManagedAgentsTextBlock __all__ = ["BetaManagedAgentsDeltaContent"] class BetaManagedAgentsDeltaContent(BaseModel): content: BetaManagedAgentsTextBlock """Regular text content.""" type: Literal["content_delta"] index: Optional[int] = None """Which entry in the previewed event's content array this fragment lands in. Insert content as that entry when the index is new; append to the existing entry otherwise. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_delta_event.py000066400000000000000000000023721523216435200314610ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel from .beta_managed_agents_delta_content import BetaManagedAgentsDeltaContent __all__ = ["BetaManagedAgentsDeltaEvent"] class BetaManagedAgentsDeltaEvent(BaseModel): """An incremental update to an event that is still being streamed. Deltas are best-effort and may stop early; when the buffered event with id == event_id is produced it carries the complete content. A model request that ends early (an error or interrupt) produces no buffered event — its terminal span.model_request_end closes the preview. Only sent on stream connections that opt in via event_deltas; never appears in event history. """ delta: BetaManagedAgentsDeltaContent """One fragment of the previewed event. The delta type is named for the previewed event's field it streams into: agent.message events stream content_delta fragments, each a partial element of the content array. """ event_id: str """The id of the event being previewed. Matches event.id on the corresponding event_start and the buffered event that reconciles the preview. """ type: Literal["event_delta"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_delta_type.py000066400000000000000000000004061523216435200313150ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal, TypeAlias __all__ = ["BetaManagedAgentsDeltaType"] BetaManagedAgentsDeltaType: TypeAlias = Literal["agent.message", "agent.thinking"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_deployment.py000066400000000000000000000050641523216435200313500ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Dict, List, Optional from datetime import datetime from typing_extensions import Literal from ..._models import BaseModel from .beta_managed_agents_schedule import BetaManagedAgentsSchedule from .beta_managed_agents_agent_reference import BetaManagedAgentsAgentReference from .beta_managed_agents_deployment_status import BetaManagedAgentsDeploymentStatus from .beta_managed_agents_session_resource_config import BetaManagedAgentsSessionResourceConfig from .beta_managed_agents_deployment_initial_event import BetaManagedAgentsDeploymentInitialEvent from .beta_managed_agents_deployment_paused_reason import BetaManagedAgentsDeploymentPausedReason __all__ = ["BetaManagedAgentsDeployment"] class BetaManagedAgentsDeployment(BaseModel): """ A deployment is a configured instance of an agent — it binds the agent to everything needed to run it autonomously: an environment, credentials, initial events, and an optional schedule. """ id: str """Unique identifier for this deployment.""" agent: BetaManagedAgentsAgentReference """A resolved agent reference with a concrete version.""" archived_at: Optional[datetime] = None """A timestamp in RFC 3339 format""" created_at: datetime """A timestamp in RFC 3339 format""" description: Optional[str] = None """Description of what the deployment does.""" environment_id: str """ID of the `environment` where sessions run.""" initial_events: List[BetaManagedAgentsDeploymentInitialEvent] """Events sent to each session immediately after creation.""" metadata: Dict[str, str] """Arbitrary key-value metadata. Maximum 16 pairs.""" name: str """Human-readable name.""" paused_reason: Optional[BetaManagedAgentsDeploymentPausedReason] = None """Why a deployment is paused. Non-null exactly when `status` is `paused`.""" resources: List[BetaManagedAgentsSessionResourceConfig] """Resources attached to sessions created from this deployment. Echoes the input minus write-only credentials. """ schedule: Optional[BetaManagedAgentsSchedule] = None """5-field POSIX cron schedule with computed runtime timestamps.""" status: BetaManagedAgentsDeploymentStatus """Lifecycle status of a deployment.""" type: Literal["deployment"] updated_at: datetime """A timestamp in RFC 3339 format""" vault_ids: List[str] """ Vault IDs supplying stored credentials for sessions created from this deployment. """ beta_managed_agents_deployment_initial_event.py000066400000000000000000000015641523216435200342040ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from ..._utils import PropertyInfo from .beta_managed_agents_deployment_user_message_event import BetaManagedAgentsDeploymentUserMessageEvent from .beta_managed_agents_deployment_system_message_event import BetaManagedAgentsDeploymentSystemMessageEvent from .beta_managed_agents_deployment_user_define_outcome_event import BetaManagedAgentsDeploymentUserDefineOutcomeEvent __all__ = ["BetaManagedAgentsDeploymentInitialEvent"] BetaManagedAgentsDeploymentInitialEvent: TypeAlias = Annotated[ Union[ BetaManagedAgentsDeploymentUserMessageEvent, BetaManagedAgentsDeploymentUserDefineOutcomeEvent, BetaManagedAgentsDeploymentSystemMessageEvent, ], PropertyInfo(discriminator="type"), ] beta_managed_agents_deployment_initial_event_params.py000066400000000000000000000014431523216435200355430ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from typing_extensions import TypeAlias from .sessions.beta_managed_agents_user_message_event_params import BetaManagedAgentsUserMessageEventParams from .sessions.beta_managed_agents_system_message_event_params import BetaManagedAgentsSystemMessageEventParams from .sessions.beta_managed_agents_user_define_outcome_event_params import BetaManagedAgentsUserDefineOutcomeEventParams __all__ = ["BetaManagedAgentsDeploymentInitialEventParams"] BetaManagedAgentsDeploymentInitialEventParams: TypeAlias = Union[ BetaManagedAgentsUserMessageEventParams, BetaManagedAgentsUserDefineOutcomeEventParams, BetaManagedAgentsSystemMessageEventParams, ] beta_managed_agents_deployment_paused_reason.py000066400000000000000000000012551523216435200341770ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from ..._utils import PropertyInfo from .beta_managed_agents_error_deployment_paused_reason import BetaManagedAgentsErrorDeploymentPausedReason from .beta_managed_agents_manual_deployment_paused_reason import BetaManagedAgentsManualDeploymentPausedReason __all__ = ["BetaManagedAgentsDeploymentPausedReason"] BetaManagedAgentsDeploymentPausedReason: TypeAlias = Annotated[ Union[BetaManagedAgentsManualDeploymentPausedReason, BetaManagedAgentsErrorDeploymentPausedReason], PropertyInfo(discriminator="type"), ] beta_managed_agents_deployment_paused_reason_error.py000066400000000000000000000067201523216435200354120ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from ..._utils import PropertyInfo from .beta_managed_agents_unknown_deployment_paused_reason_error import ( BetaManagedAgentsUnknownDeploymentPausedReasonError, ) from .beta_managed_agents_agent_archived_deployment_paused_reason_error import ( BetaManagedAgentsAgentArchivedDeploymentPausedReasonError, ) from .beta_managed_agents_file_not_found_deployment_paused_reason_error import ( BetaManagedAgentsFileNotFoundDeploymentPausedReasonError, ) from .beta_managed_agents_vault_archived_deployment_paused_reason_error import ( BetaManagedAgentsVaultArchivedDeploymentPausedReasonError, ) from .beta_managed_agents_skill_not_found_deployment_paused_reason_error import ( BetaManagedAgentsSkillNotFoundDeploymentPausedReasonError, ) from .beta_managed_agents_vault_not_found_deployment_paused_reason_error import ( BetaManagedAgentsVaultNotFoundDeploymentPausedReasonError, ) from .beta_managed_agents_mcp_egress_blocked_deployment_paused_reason_error import ( BetaManagedAgentsMCPEgressBlockedDeploymentPausedReasonError, ) from .beta_managed_agents_workspace_archived_deployment_paused_reason_error import ( BetaManagedAgentsWorkspaceArchivedDeploymentPausedReasonError, ) from .beta_managed_agents_environment_archived_deployment_paused_reason_error import ( BetaManagedAgentsEnvironmentArchivedDeploymentPausedReasonError, ) from .beta_managed_agents_environment_not_found_deployment_paused_reason_error import ( BetaManagedAgentsEnvironmentNotFoundDeploymentPausedReasonError, ) from .beta_managed_agents_memory_store_archived_deployment_paused_reason_error import ( BetaManagedAgentsMemoryStoreArchivedDeploymentPausedReasonError, ) from .beta_managed_agents_organization_disabled_deployment_paused_reason_error import ( BetaManagedAgentsOrganizationDisabledDeploymentPausedReasonError, ) from .beta_managed_agents_session_resource_not_found_deployment_paused_reason_error import ( BetaManagedAgentsSessionResourceNotFoundDeploymentPausedReasonError, ) from .beta_managed_agents_self_hosted_resources_unsupported_deployment_paused_reason_error import ( BetaManagedAgentsSelfHostedResourcesUnsupportedDeploymentPausedReasonError, ) __all__ = ["BetaManagedAgentsDeploymentPausedReasonError"] BetaManagedAgentsDeploymentPausedReasonError: TypeAlias = Annotated[ Union[ BetaManagedAgentsEnvironmentArchivedDeploymentPausedReasonError, BetaManagedAgentsAgentArchivedDeploymentPausedReasonError, BetaManagedAgentsEnvironmentNotFoundDeploymentPausedReasonError, BetaManagedAgentsVaultNotFoundDeploymentPausedReasonError, BetaManagedAgentsFileNotFoundDeploymentPausedReasonError, BetaManagedAgentsSessionResourceNotFoundDeploymentPausedReasonError, BetaManagedAgentsWorkspaceArchivedDeploymentPausedReasonError, BetaManagedAgentsOrganizationDisabledDeploymentPausedReasonError, BetaManagedAgentsMemoryStoreArchivedDeploymentPausedReasonError, BetaManagedAgentsSkillNotFoundDeploymentPausedReasonError, BetaManagedAgentsVaultArchivedDeploymentPausedReasonError, BetaManagedAgentsUnknownDeploymentPausedReasonError, BetaManagedAgentsSelfHostedResourcesUnsupportedDeploymentPausedReasonError, BetaManagedAgentsMCPEgressBlockedDeploymentPausedReasonError, ], PropertyInfo(discriminator="type"), ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_deployment_run.py000066400000000000000000000101151523216435200322250ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union, Optional from datetime import datetime from typing_extensions import Literal, Annotated, TypeAlias from ..._utils import PropertyInfo from ..._models import BaseModel from .beta_managed_agents_agent_reference import BetaManagedAgentsAgentReference from .beta_managed_agents_trigger_context import BetaManagedAgentsTriggerContext from .beta_managed_agents_unknown_run_error import BetaManagedAgentsUnknownRunError from .beta_managed_agents_agent_archived_run_error import BetaManagedAgentsAgentArchivedRunError from .beta_managed_agents_file_not_found_run_error import BetaManagedAgentsFileNotFoundRunError from .beta_managed_agents_vault_archived_run_error import BetaManagedAgentsVaultArchivedRunError from .beta_managed_agents_skill_not_found_run_error import BetaManagedAgentsSkillNotFoundRunError from .beta_managed_agents_vault_not_found_run_error import BetaManagedAgentsVaultNotFoundRunError from .beta_managed_agents_mcp_egress_blocked_run_error import BetaManagedAgentsMCPEgressBlockedRunError from .beta_managed_agents_workspace_archived_run_error import BetaManagedAgentsWorkspaceArchivedRunError from .beta_managed_agents_environment_archived_run_error import BetaManagedAgentsEnvironmentArchivedRunError from .beta_managed_agents_session_rate_limited_run_error import BetaManagedAgentsSessionRateLimitedRunError from .beta_managed_agents_environment_not_found_run_error import BetaManagedAgentsEnvironmentNotFoundRunError from .beta_managed_agents_memory_store_archived_run_error import BetaManagedAgentsMemoryStoreArchivedRunError from .beta_managed_agents_organization_disabled_run_error import BetaManagedAgentsOrganizationDisabledRunError from .beta_managed_agents_session_creation_rejected_run_error import BetaManagedAgentsSessionCreationRejectedRunError from .beta_managed_agents_session_resource_not_found_run_error import BetaManagedAgentsSessionResourceNotFoundRunError from .beta_managed_agents_self_hosted_resources_unsupported_run_error import ( BetaManagedAgentsSelfHostedResourcesUnsupportedRunError, ) __all__ = ["BetaManagedAgentsDeploymentRun", "Error"] Error: TypeAlias = Annotated[ Union[ BetaManagedAgentsEnvironmentArchivedRunError, BetaManagedAgentsAgentArchivedRunError, BetaManagedAgentsEnvironmentNotFoundRunError, BetaManagedAgentsVaultNotFoundRunError, BetaManagedAgentsVaultArchivedRunError, BetaManagedAgentsFileNotFoundRunError, BetaManagedAgentsMemoryStoreArchivedRunError, BetaManagedAgentsSkillNotFoundRunError, BetaManagedAgentsSessionResourceNotFoundRunError, BetaManagedAgentsWorkspaceArchivedRunError, BetaManagedAgentsOrganizationDisabledRunError, BetaManagedAgentsSessionRateLimitedRunError, BetaManagedAgentsSessionCreationRejectedRunError, BetaManagedAgentsUnknownRunError, BetaManagedAgentsSelfHostedResourcesUnsupportedRunError, BetaManagedAgentsMCPEgressBlockedRunError, None, ], PropertyInfo(discriminator="type"), ] class BetaManagedAgentsDeploymentRun(BaseModel): """A persistent, append-only record of a single deployment execution. Records session creation success or failure — no session lifecycle tracking. """ id: str """Unique identifier for this run (`drun_...`).""" agent: BetaManagedAgentsAgentReference """A resolved agent reference with a concrete version.""" created_at: datetime """A timestamp in RFC 3339 format""" deployment_id: str """ID of the deployment that produced this run.""" error: Optional[Error] = None """Why the run failed to create a session. The type identifies the failure; message is human-readable detail. """ session_id: Optional[str] = None """Populated on success. Null on creation failure. Exactly one of session_id or error is non-null. """ trigger_context: BetaManagedAgentsTriggerContext """Describes what triggered a deployment run, with trigger-specific metadata.""" type: Literal["deployment_run"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_deployment_status.py000066400000000000000000000004051523216435200327450ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal, TypeAlias __all__ = ["BetaManagedAgentsDeploymentStatus"] BetaManagedAgentsDeploymentStatus: TypeAlias = Literal["active", "paused"] beta_managed_agents_deployment_system_message_event.py000066400000000000000000000013651523216435200356020ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List from typing_extensions import Literal from ..._models import BaseModel from .beta_managed_agents_system_content_block import BetaManagedAgentsSystemContentBlock __all__ = ["BetaManagedAgentsDeploymentSystemMessageEvent"] class BetaManagedAgentsDeploymentSystemMessageEvent(BaseModel): """ Privileged context for the accompanying turn and all subsequent turns, appended to the session's system context as a `role: "system"` turn rather than replacing the top-level system prompt. """ content: List[BetaManagedAgentsSystemContentBlock] """System content blocks to append. Text-only.""" type: Literal["system.message"] beta_managed_agents_deployment_user_define_outcome_event.py000066400000000000000000000021241523216435200365670ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union, Optional from typing_extensions import Literal, Annotated, TypeAlias from ..._utils import PropertyInfo from ..._models import BaseModel from .sessions.beta_managed_agents_file_rubric import BetaManagedAgentsFileRubric from .sessions.beta_managed_agents_text_rubric import BetaManagedAgentsTextRubric __all__ = ["BetaManagedAgentsDeploymentUserDefineOutcomeEvent", "Rubric"] Rubric: TypeAlias = Annotated[ Union[BetaManagedAgentsFileRubric, BetaManagedAgentsTextRubric], PropertyInfo(discriminator="type") ] class BetaManagedAgentsDeploymentUserDefineOutcomeEvent(BaseModel): """An outcome the agent should work toward. The agent begins work on receipt.""" description: str """What the agent should produce. This is the task specification.""" rubric: Rubric """Rubric for grading the quality of an outcome.""" type: Literal["user.define_outcome"] max_iterations: Optional[int] = None """Eval→revision cycles before giving up. Default 3, max 20.""" beta_managed_agents_deployment_user_message_event.py000066400000000000000000000017131523216435200352310ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Union from typing_extensions import Literal, Annotated, TypeAlias from ..._utils import PropertyInfo from ..._models import BaseModel from .sessions.beta_managed_agents_text_block import BetaManagedAgentsTextBlock from .sessions.beta_managed_agents_image_block import BetaManagedAgentsImageBlock from .sessions.beta_managed_agents_document_block import BetaManagedAgentsDocumentBlock __all__ = ["BetaManagedAgentsDeploymentUserMessageEvent", "Content"] Content: TypeAlias = Annotated[ Union[BetaManagedAgentsTextBlock, BetaManagedAgentsImageBlock, BetaManagedAgentsDocumentBlock], PropertyInfo(discriminator="type"), ] class BetaManagedAgentsDeploymentUserMessageEvent(BaseModel): """A user message sent to the session.""" content: List[Content] """Array of content blocks for the user message.""" type: Literal["user.message"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_effort_high.py000066400000000000000000000005041523216435200314460ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsEffortHigh"] class BetaManagedAgentsEffortHigh(BaseModel): """High effort. Favors reasoning depth.""" type: Literal["high"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_effort_high_param.py000066400000000000000000000005741523216435200326350ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsEffortHighParam"] class BetaManagedAgentsEffortHighParam(TypedDict, total=False): """High effort. Favors reasoning depth.""" type: Required[Literal["high"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_effort_low.py000066400000000000000000000005151523216435200313320ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsEffortLow"] class BetaManagedAgentsEffortLow(BaseModel): """Low effort. Favors latency over reasoning depth.""" type: Literal["low"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_effort_low_param.py000066400000000000000000000006051523216435200325120ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsEffortLowParam"] class BetaManagedAgentsEffortLowParam(TypedDict, total=False): """Low effort. Favors latency over reasoning depth.""" type: Required[Literal["low"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_effort_max.py000066400000000000000000000005211523216435200313130ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsEffortMax"] class BetaManagedAgentsEffortMax(BaseModel): """Maximum effort. Favors reasoning depth over latency.""" type: Literal["max"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_effort_max_param.py000066400000000000000000000006111523216435200324730ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsEffortMaxParam"] class BetaManagedAgentsEffortMaxParam(TypedDict, total=False): """Maximum effort. Favors reasoning depth over latency.""" type: Required[Literal["max"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_effort_medium.py000066400000000000000000000005321523216435200320100ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsEffortMedium"] class BetaManagedAgentsEffortMedium(BaseModel): """Medium effort. Balances latency and reasoning depth.""" type: Literal["medium"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_effort_medium_param.py000066400000000000000000000006221523216435200331700ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsEffortMediumParam"] class BetaManagedAgentsEffortMediumParam(TypedDict, total=False): """Medium effort. Balances latency and reasoning depth.""" type: Required[Literal["medium"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_effort_xhigh.py000066400000000000000000000005271523216435200316430ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsEffortXhigh"] class BetaManagedAgentsEffortXhigh(BaseModel): """Extra-high effort. Not all models accept this level.""" type: Literal["xhigh"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_effort_xhigh_param.py000066400000000000000000000006171523216435200330230ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsEffortXhighParam"] class BetaManagedAgentsEffortXhighParam(TypedDict, total=False): """Extra-high effort. Not all models accept this level.""" type: Required[Literal["xhigh"]] beta_managed_agents_environment_archived_deployment_paused_reason_error.py000066400000000000000000000006501523216435200416770ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsEnvironmentArchivedDeploymentPausedReasonError"] class BetaManagedAgentsEnvironmentArchivedDeploymentPausedReasonError(BaseModel): """The deployment's environment was archived.""" type: Literal["environment_archived_error"] beta_managed_agents_environment_archived_run_error.py000066400000000000000000000007001523216435200354070ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsEnvironmentArchivedRunError"] class BetaManagedAgentsEnvironmentArchivedRunError(BaseModel): """The deployment's environment was archived.""" message: str """Human-readable error description.""" type: Literal["environment_archived_error"] beta_managed_agents_environment_not_found_deployment_paused_reason_error.py000066400000000000000000000006551523216435200421120ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsEnvironmentNotFoundDeploymentPausedReasonError"] class BetaManagedAgentsEnvironmentNotFoundDeploymentPausedReasonError(BaseModel): """The deployment's environment no longer exists.""" type: Literal["environment_not_found_error"] beta_managed_agents_environment_not_found_run_error.py000066400000000000000000000007051523216435200356220ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsEnvironmentNotFoundRunError"] class BetaManagedAgentsEnvironmentNotFoundRunError(BaseModel): """The deployment's environment no longer exists.""" message: str """Human-readable error description.""" type: Literal["environment_not_found_error"] beta_managed_agents_error_deployment_paused_reason.py000066400000000000000000000012201523216435200354000ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel from .beta_managed_agents_deployment_paused_reason_error import BetaManagedAgentsDeploymentPausedReasonError __all__ = ["BetaManagedAgentsErrorDeploymentPausedReason"] class BetaManagedAgentsErrorDeploymentPausedReason(BaseModel): """A scheduled fire recorded a failed run whose error auto-pauses the deployment.""" error: BetaManagedAgentsDeploymentPausedReasonError """The error that triggered an auto-pause. Matches the failed run's `error.type`.""" type: Literal["error"] beta_managed_agents_file_not_found_deployment_paused_reason_error.py000066400000000000000000000006501523216435200404600ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsFileNotFoundDeploymentPausedReasonError"] class BetaManagedAgentsFileNotFoundDeploymentPausedReasonError(BaseModel): """A file resource referenced by the deployment no longer exists.""" type: Literal["file_not_found_error"] beta_managed_agents_file_not_found_run_error.py000066400000000000000000000007001523216435200341700ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsFileNotFoundRunError"] class BetaManagedAgentsFileNotFoundRunError(BaseModel): """A file resource referenced by the deployment no longer exists.""" message: str """Human-readable error description.""" type: Literal["file_not_found_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_file_resource_config.py000066400000000000000000000010621523216435200333350ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsFileResourceConfig"] class BetaManagedAgentsFileResourceConfig(BaseModel): """A file mounted into each session's container.""" file_id: str """ID of a previously uploaded file.""" type: Literal["file"] mount_path: Optional[str] = None """Mount path in the container. Defaults to `/mnt/session/uploads/`.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_file_resource_params.py000066400000000000000000000011571523216435200333600ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsFileResourceParams"] class BetaManagedAgentsFileResourceParams(TypedDict, total=False): """Mount a file uploaded via the Files API into the session.""" file_id: Required[str] """ID of a previously uploaded file.""" type: Required[Literal["file"]] mount_path: Optional[str] """Mount path in the container. Defaults to `/mnt/session/uploads/`.""" beta_managed_agents_github_repository_resource_config.py000066400000000000000000000022361523216435200361240ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union, Optional from typing_extensions import Literal, Annotated, TypeAlias from ..._utils import PropertyInfo from ..._models import BaseModel from .beta_managed_agents_branch_checkout import BetaManagedAgentsBranchCheckout from .beta_managed_agents_commit_checkout import BetaManagedAgentsCommitCheckout __all__ = ["BetaManagedAgentsGitHubRepositoryResourceConfig", "Checkout"] Checkout: TypeAlias = Annotated[ Union[BetaManagedAgentsBranchCheckout, BetaManagedAgentsCommitCheckout, None], PropertyInfo(discriminator="type") ] class BetaManagedAgentsGitHubRepositoryResourceConfig(BaseModel): """A GitHub repository mounted into each session's container. The authorization token is write-only and never returned. """ type: Literal["github_repository"] url: str """Github URL of the repository""" checkout: Optional[Checkout] = None """Branch or commit to check out. Defaults to the repository's default branch.""" mount_path: Optional[str] = None """Mount path in the container. Defaults to `/workspace/`.""" beta_managed_agents_github_repository_resource_params.py000066400000000000000000000022441523216435200361410ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .beta_managed_agents_branch_checkout_param import BetaManagedAgentsBranchCheckoutParam from .beta_managed_agents_commit_checkout_param import BetaManagedAgentsCommitCheckoutParam __all__ = ["BetaManagedAgentsGitHubRepositoryResourceParams", "Checkout"] Checkout: TypeAlias = Union[BetaManagedAgentsBranchCheckoutParam, BetaManagedAgentsCommitCheckoutParam] class BetaManagedAgentsGitHubRepositoryResourceParams(TypedDict, total=False): """Mount a GitHub repository into the session's container.""" authorization_token: Required[str] """GitHub authorization token used to clone the repository.""" type: Required[Literal["github_repository"]] url: Required[str] """Github URL of the repository""" checkout: Optional[Checkout] """Branch or commit to check out. Defaults to the repository's default branch.""" mount_path: Optional[str] """Mount path in the container. Defaults to `/workspace/`.""" beta_managed_agents_manual_deployment_paused_reason.py000066400000000000000000000005761523216435200355410ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsManualDeploymentPausedReason"] class BetaManagedAgentsManualDeploymentPausedReason(BaseModel): """The caller invoked the pause endpoint on the deployment.""" type: Literal["manual"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_manual_trigger_context.py000066400000000000000000000006231523216435200337300ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsManualTriggerContext"] class BetaManagedAgentsManualTriggerContext(BaseModel): """ The run was started manually by creating a session directly against the deployment. """ type: Literal["manual"] beta_managed_agents_mcp_egress_blocked_deployment_paused_reason_error.py000066400000000000000000000007411523216435200413010ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsMCPEgressBlockedDeploymentPausedReasonError"] class BetaManagedAgentsMCPEgressBlockedDeploymentPausedReasonError(BaseModel): """ An MCP server host used by the deployment's agent is blocked by the environment's network policy. """ type: Literal["mcp_egress_blocked_error"] beta_managed_agents_mcp_egress_blocked_run_error.py000066400000000000000000000007711523216435200350200ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsMCPEgressBlockedRunError"] class BetaManagedAgentsMCPEgressBlockedRunError(BaseModel): """ An MCP server host used by the deployment's agent is blocked by the environment's network policy. """ message: str """Human-readable error description.""" type: Literal["mcp_egress_blocked_error"] beta_managed_agents_mcp_server_url_definition.py000066400000000000000000000006211523216435200343420ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsMCPServerURLDefinition"] class BetaManagedAgentsMCPServerURLDefinition(BaseModel): """URL-based MCP server connection as returned in API responses.""" name: str type: Literal["url"] url: str anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_mcp_tool_config.py000066400000000000000000000015361523216435200323310ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from ..._utils import PropertyInfo from ..._models import BaseModel from .beta_managed_agents_always_ask_policy import BetaManagedAgentsAlwaysAskPolicy from .beta_managed_agents_always_allow_policy import BetaManagedAgentsAlwaysAllowPolicy __all__ = ["BetaManagedAgentsMCPToolConfig", "PermissionPolicy"] PermissionPolicy: TypeAlias = Annotated[ Union[BetaManagedAgentsAlwaysAllowPolicy, BetaManagedAgentsAlwaysAskPolicy], PropertyInfo(discriminator="type") ] class BetaManagedAgentsMCPToolConfig(BaseModel): """Resolved configuration for a specific MCP tool.""" enabled: bool name: str permission_policy: PermissionPolicy """Permission policy for tool execution.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_mcp_tool_config_params.py000066400000000000000000000020031523216435200336620ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from typing_extensions import Required, TypeAlias, TypedDict from .beta_managed_agents_always_ask_policy_param import BetaManagedAgentsAlwaysAskPolicyParam from .beta_managed_agents_always_allow_policy_param import BetaManagedAgentsAlwaysAllowPolicyParam __all__ = ["BetaManagedAgentsMCPToolConfigParams", "PermissionPolicy"] PermissionPolicy: TypeAlias = Union[BetaManagedAgentsAlwaysAllowPolicyParam, BetaManagedAgentsAlwaysAskPolicyParam] class BetaManagedAgentsMCPToolConfigParams(TypedDict, total=False): """Configuration override for a specific MCP tool.""" name: Required[str] """Name of the MCP tool to configure. 1-128 characters.""" enabled: Optional[bool] """Whether this tool is enabled. Overrides the `default_config` setting.""" permission_policy: Optional[PermissionPolicy] """Permission policy for tool execution.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_mcp_toolset.py000066400000000000000000000012761523216435200315210ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List from typing_extensions import Literal from ..._models import BaseModel from .beta_managed_agents_mcp_tool_config import BetaManagedAgentsMCPToolConfig from .beta_managed_agents_mcp_toolset_default_config import BetaManagedAgentsMCPToolsetDefaultConfig __all__ = ["BetaManagedAgentsMCPToolset"] class BetaManagedAgentsMCPToolset(BaseModel): configs: List[BetaManagedAgentsMCPToolConfig] default_config: BetaManagedAgentsMCPToolsetDefaultConfig """Resolved default configuration for all tools from an MCP server.""" mcp_server_name: str type: Literal["mcp_toolset"] beta_managed_agents_mcp_toolset_default_config.py000066400000000000000000000015641523216435200344730ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from ..._utils import PropertyInfo from ..._models import BaseModel from .beta_managed_agents_always_ask_policy import BetaManagedAgentsAlwaysAskPolicy from .beta_managed_agents_always_allow_policy import BetaManagedAgentsAlwaysAllowPolicy __all__ = ["BetaManagedAgentsMCPToolsetDefaultConfig", "PermissionPolicy"] PermissionPolicy: TypeAlias = Annotated[ Union[BetaManagedAgentsAlwaysAllowPolicy, BetaManagedAgentsAlwaysAskPolicy], PropertyInfo(discriminator="type") ] class BetaManagedAgentsMCPToolsetDefaultConfig(BaseModel): """Resolved default configuration for all tools from an MCP server.""" enabled: bool permission_policy: PermissionPolicy """Permission policy for tool execution.""" beta_managed_agents_mcp_toolset_default_config_params.py000066400000000000000000000017001523216435200360260ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from typing_extensions import TypeAlias, TypedDict from .beta_managed_agents_always_ask_policy_param import BetaManagedAgentsAlwaysAskPolicyParam from .beta_managed_agents_always_allow_policy_param import BetaManagedAgentsAlwaysAllowPolicyParam __all__ = ["BetaManagedAgentsMCPToolsetDefaultConfigParams", "PermissionPolicy"] PermissionPolicy: TypeAlias = Union[BetaManagedAgentsAlwaysAllowPolicyParam, BetaManagedAgentsAlwaysAskPolicyParam] class BetaManagedAgentsMCPToolsetDefaultConfigParams(TypedDict, total=False): """Default configuration for all tools from an MCP server.""" enabled: Optional[bool] """Whether tools are enabled by default. Defaults to true if not specified.""" permission_policy: Optional[PermissionPolicy] """Permission policy for tool execution.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_mcp_toolset_params.py000066400000000000000000000020531523216435200330560ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Iterable, Optional from typing_extensions import Literal, Required, TypedDict from .beta_managed_agents_mcp_tool_config_params import BetaManagedAgentsMCPToolConfigParams from .beta_managed_agents_mcp_toolset_default_config_params import BetaManagedAgentsMCPToolsetDefaultConfigParams __all__ = ["BetaManagedAgentsMCPToolsetParams"] class BetaManagedAgentsMCPToolsetParams(TypedDict, total=False): """Configuration for tools from an MCP server defined in `mcp_servers`.""" mcp_server_name: Required[str] """Name of the MCP server. Must match a server name from the mcp_servers array. 1-255 characters. """ type: Required[Literal["mcp_toolset"]] configs: Iterable[BetaManagedAgentsMCPToolConfigParams] """Per-tool configuration overrides.""" default_config: Optional[BetaManagedAgentsMCPToolsetDefaultConfigParams] """Default configuration for all tools from an MCP server.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_memory_store.py000066400000000000000000000032561523216435200317150ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Dict, Optional from datetime import datetime from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsMemoryStore"] class BetaManagedAgentsMemoryStore(BaseModel): """A `memory_store`: a named container for agent memories, scoped to a workspace. Attach a store to a session via `resources[]` to mount it as a directory the agent can read and write. """ id: str """Unique identifier for the memory store (a `memstore_...` tagged ID). Use this when attaching the store to a session, or in the `{memory_store_id}` path parameter of subsequent calls. """ created_at: datetime """A timestamp in RFC 3339 format""" name: str """Human-readable name for the store. 1–255 characters. The store's mount-path slug under `/mnt/memory/` is derived from this name. """ type: Literal["memory_store"] updated_at: datetime """A timestamp in RFC 3339 format""" archived_at: Optional[datetime] = None """A timestamp in RFC 3339 format""" description: Optional[str] = None """Free-text description of what the store contains, up to 1024 characters. Included in the agent's system prompt when the store is attached, so word it to be useful to the agent. Empty string when unset. """ metadata: Optional[Dict[str, str]] = None """ Arbitrary key-value tags for your own bookkeeping (such as the end user a store belongs to). Up to 16 pairs; keys 1–64 characters; values up to 512 characters. Returned on retrieve/list but not filterable. """ beta_managed_agents_memory_store_archived_deployment_paused_reason_error.py000066400000000000000000000006671523216435200420670ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsMemoryStoreArchivedDeploymentPausedReasonError"] class BetaManagedAgentsMemoryStoreArchivedDeploymentPausedReasonError(BaseModel): """A memory store referenced by the deployment is archived.""" type: Literal["memory_store_archived_error"] beta_managed_agents_memory_store_archived_run_error.py000066400000000000000000000007171523216435200355770ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsMemoryStoreArchivedRunError"] class BetaManagedAgentsMemoryStoreArchivedRunError(BaseModel): """A memory store referenced by the deployment is archived.""" message: str """Human-readable error description.""" type: Literal["memory_store_archived_error"] beta_managed_agents_memory_store_resource_config.py000066400000000000000000000015541523216435200350710ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsMemoryStoreResourceConfig"] class BetaManagedAgentsMemoryStoreResourceConfig(BaseModel): """A memory store attached to each session created from this deployment.""" memory_store_id: str """The memory store ID (memstore\\__...). Must belong to the caller's organization and workspace. """ type: Literal["memory_store"] access: Optional[Literal["read_write", "read_only"]] = None """Access mode for an attached memory store.""" instructions: Optional[str] = None """Per-attachment guidance for the agent on how to use this store. Rendered into the memory section of the system prompt. Max 4096 chars. """ beta_managed_agents_memory_store_resource_param.py000066400000000000000000000016131523216435200347200ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsMemoryStoreResourceParam"] class BetaManagedAgentsMemoryStoreResourceParam(TypedDict, total=False): """Parameters for attaching a memory store to an agent session.""" memory_store_id: Required[str] """The memory store ID (memstore\\__...). Must belong to the caller's organization and workspace. """ type: Required[Literal["memory_store"]] access: Optional[Literal["read_write", "read_only"]] """Access mode for an attached memory store.""" instructions: Optional[str] """Per-attachment guidance for the agent on how to use this store. Rendered into the memory section of the system prompt. Max 4096 chars. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_model.py000066400000000000000000000012211523216435200302570ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Literal, TypeAlias __all__ = ["BetaManagedAgentsModel"] BetaManagedAgentsModel: TypeAlias = Union[ Literal[ "claude-sonnet-5", "claude-fable-5", "claude-opus-5", "claude-opus-4-8", "claude-opus-4-7", "claude-opus-4-6", "claude-sonnet-4-6", "claude-haiku-4-5", "claude-haiku-4-5-20251001", "claude-opus-4-5", "claude-opus-4-5-20251101", "claude-sonnet-4-5", "claude-sonnet-4-5-20250929", ], str, ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_model_config.py000066400000000000000000000033021523216435200316060ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union, Optional from typing_extensions import Literal, Annotated, TypeAlias from ..._utils import PropertyInfo from ..._models import BaseModel from .beta_managed_agents_model import BetaManagedAgentsModel from .beta_managed_agents_effort_low import BetaManagedAgentsEffortLow from .beta_managed_agents_effort_max import BetaManagedAgentsEffortMax from .beta_managed_agents_effort_high import BetaManagedAgentsEffortHigh from .beta_managed_agents_effort_xhigh import BetaManagedAgentsEffortXhigh from .beta_managed_agents_effort_medium import BetaManagedAgentsEffortMedium __all__ = ["BetaManagedAgentsModelConfig", "Effort"] Effort: TypeAlias = Annotated[ Union[ BetaManagedAgentsEffortLow, BetaManagedAgentsEffortMedium, BetaManagedAgentsEffortHigh, BetaManagedAgentsEffortXhigh, BetaManagedAgentsEffortMax, ], PropertyInfo(discriminator="type"), ] class BetaManagedAgentsModelConfig(BaseModel): """Model identifier and configuration.""" id: BetaManagedAgentsModel """The model that will power your agent. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. """ effort: Optional[Effort] = None """How hard Claude works on each turn. Sets `output_config.effort` on every Messages call the session makes. """ speed: Optional[Literal["standard", "fast"]] = None """Inference speed mode. `fast` provides significantly faster output token generation at premium pricing. Not all models support `fast`; invalid combinations are rejected at create time. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_model_config_params.py000066400000000000000000000036431523216435200331610ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .beta_managed_agents_model_param import BetaManagedAgentsModelParam from .beta_managed_agents_effort_low_param import BetaManagedAgentsEffortLowParam from .beta_managed_agents_effort_max_param import BetaManagedAgentsEffortMaxParam from .beta_managed_agents_effort_high_param import BetaManagedAgentsEffortHighParam from .beta_managed_agents_effort_xhigh_param import BetaManagedAgentsEffortXhighParam from .beta_managed_agents_effort_medium_param import BetaManagedAgentsEffortMediumParam __all__ = ["BetaManagedAgentsModelConfigParams", "Effort"] Effort: TypeAlias = Union[ Literal["low", "medium", "high", "xhigh", "max"], BetaManagedAgentsEffortLowParam, BetaManagedAgentsEffortMediumParam, BetaManagedAgentsEffortHighParam, BetaManagedAgentsEffortXhighParam, BetaManagedAgentsEffortMaxParam, ] class BetaManagedAgentsModelConfigParams(TypedDict, total=False): """An object that defines additional configuration control over model use""" id: Required[BetaManagedAgentsModelParam] """The model that will power your agent. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. """ effort: Optional[Effort] """How hard Claude works on each inference call. Accepts a bare level string (`"high"`) or `{"type": "high"}`. On create, omitting it resolves the per-model default; on update, omitting it leaves the stored value unchanged. """ speed: Optional[Literal["standard", "fast"]] """Inference speed mode. `fast` provides significantly faster output token generation at premium pricing. Not all models support `fast`; invalid combinations are rejected at create time. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_model_param.py000066400000000000000000000012771523216435200314520ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from typing_extensions import Literal, TypeAlias __all__ = ["BetaManagedAgentsModelParam"] BetaManagedAgentsModelParam: TypeAlias = Union[ Literal[ "claude-sonnet-5", "claude-fable-5", "claude-opus-5", "claude-opus-4-8", "claude-opus-4-7", "claude-opus-4-6", "claude-sonnet-4-6", "claude-haiku-4-5", "claude-haiku-4-5-20251001", "claude-opus-4-5", "claude-opus-4-5-20251101", "claude-sonnet-4-5", "claude-sonnet-4-5-20250929", ], str, ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_multiagent.py000066400000000000000000000011601523216435200313320ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List from typing_extensions import Literal from ..._models import BaseModel from .beta_managed_agents_agent_reference import BetaManagedAgentsAgentReference __all__ = ["BetaManagedAgentsMultiagent"] class BetaManagedAgentsMultiagent(BaseModel): """Resolved coordinator topology with a concrete agent roster.""" agents: List[BetaManagedAgentsAgentReference] """ Agents the coordinator may spawn as session threads, each resolved to a specific version. """ type: Literal["coordinator"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_multiagent_params.py000066400000000000000000000023051523216435200326770ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict from ..._types import SequenceNotStr from .beta_managed_agents_multiagent_roster_entry_params import BetaManagedAgentsMultiagentRosterEntryParams __all__ = ["BetaManagedAgentsMultiagentParams"] class BetaManagedAgentsMultiagentParams(TypedDict, total=False): """ A coordinator topology: the session's primary thread orchestrates work by spawning session threads, each running an agent drawn from the `agents` roster. """ agents: Required[SequenceNotStr[BetaManagedAgentsMultiagentRosterEntryParams]] """Agents the coordinator may spawn as session threads. 1–20 entries. Each entry is an agent ID string, a versioned `{"type":"agent","id","version"}` reference, or `{"type":"self"}` to allow recursive self-invocation. Entries must reference distinct agents (after resolving `self` and string forms); at most one `self`. Referenced agents must exist, must not be archived, and must not themselves have `multiagent` set (depth limit 1). """ type: Required[Literal["coordinator"]] beta_managed_agents_multiagent_roster_entry_params.py000066400000000000000000000010631523216435200354370ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from typing_extensions import TypeAlias from .beta_managed_agents_agent_params import BetaManagedAgentsAgentParams from .beta_managed_agents_multiagent_self_params import BetaManagedAgentsMultiagentSelfParams __all__ = ["BetaManagedAgentsMultiagentRosterEntryParams"] BetaManagedAgentsMultiagentRosterEntryParams: TypeAlias = Union[ str, BetaManagedAgentsAgentParams, BetaManagedAgentsMultiagentSelfParams ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_multiagent_self_params.py000066400000000000000000000007471523216435200337200ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsMultiagentSelfParams"] class BetaManagedAgentsMultiagentSelfParams(TypedDict, total=False): """Sentinel roster entry meaning "the agent that owns this configuration". Resolved server-side to a concrete agent reference. """ type: Required[Literal["self"]] beta_managed_agents_organization_disabled_deployment_paused_reason_error.py000066400000000000000000000006531523216435200420240ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsOrganizationDisabledDeploymentPausedReasonError"] class BetaManagedAgentsOrganizationDisabledDeploymentPausedReasonError(BaseModel): """The deployment's organization is disabled.""" type: Literal["organization_disabled_error"] beta_managed_agents_organization_disabled_run_error.py000066400000000000000000000007031523216435200355340ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsOrganizationDisabledRunError"] class BetaManagedAgentsOrganizationDisabledRunError(BaseModel): """The deployment's organization is disabled.""" message: str """Human-readable error description.""" type: Literal["organization_disabled_error"] beta_managed_agents_outcome_evaluation_resource.py000066400000000000000000000024021523216435200347130ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from datetime import datetime from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsOutcomeEvaluationResource"] class BetaManagedAgentsOutcomeEvaluationResource(BaseModel): """Evaluation state for a single outcome defined via a define_outcome event.""" completed_at: Optional[datetime] = None """A timestamp in RFC 3339 format""" description: str """What the agent should produce.""" explanation: Optional[str] = None """Grader's verdict text from the most recent evaluation. For satisfied, explains why criteria are met; for needs_revision (intermediate), what's missing; for failed, why unrecoverable. """ iteration: int """0-indexed revision cycle the outcome is currently on.""" outcome_id: str """Server-generated outc\\__ ID for this outcome.""" result: str """Current evaluation state. `pending` before the agent begins work; `running` while producing or revising; `evaluating` while the grader scores; `satisfied`/`max_iterations_reached`/`failed`/`interrupted` are terminal. """ type: Literal["outcome_evaluation"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_schedule.py000066400000000000000000000025351523216435200307640ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from datetime import datetime from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsSchedule"] class BetaManagedAgentsSchedule(BaseModel): """5-field POSIX cron schedule with computed runtime timestamps.""" expression: str """ 5-field POSIX cron expression: minute hour day-of-month month day-of-week (e.g., "0 9 \\** \\** 1-5" for weekdays at 9am). Day-of-week is 0-7 where 0 and 7 both mean Sunday. Extended cron syntax - seconds or year fields, and the special characters L, W, #, and ? - is not supported, nor are predefined shortcuts (@daily). """ timezone: str """IANA timezone identifier (e.g., "America/Los_Angeles", "UTC").""" type: Literal["cron"] last_run_at: Optional[datetime] = None """A timestamp in RFC 3339 format""" upcoming_runs_at: Optional[List[datetime]] = None """Up to 5 timestamps of upcoming cron occurrences. Non-empty for active and paused deployments (reflects what the schedule would do if unpaused); empty once the deployment is archived (`archived_at` set). Each fire is offset by a small per-schedule jitter, so a run will actually start at or shortly after its listed time. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_schedule_params.py000066400000000000000000000017451523216435200323310ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsScheduleParams"] class BetaManagedAgentsScheduleParams(TypedDict, total=False): """5-field POSIX cron schedule. Literal wall-clock matching in the configured timezone. """ expression: Required[str] """ 5-field POSIX cron expression: minute hour day-of-month month day-of-week (e.g., "0 9 \\** \\** 1-5" for weekdays at 9am). Day-of-week is 0-7 where 0 and 7 both mean Sunday. Extended cron syntax - seconds or year fields, and the special characters L, W, #, and ? - is not supported, nor are predefined shortcuts (@daily). """ timezone: Required[str] """Required. IANA timezone identifier (e.g., "America/Los_Angeles", "UTC"). Validated against the IANA timezone database. """ type: Required[Literal["cron"]] beta_managed_agents_schedule_trigger_context.py000066400000000000000000000007231523216435200341710ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from datetime import datetime from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsScheduleTriggerContext"] class BetaManagedAgentsScheduleTriggerContext(BaseModel): """The run was fired by the deployment's cron schedule.""" scheduled_at: datetime """A timestamp in RFC 3339 format""" type: Literal["schedule"] beta_managed_agents_self_hosted_resources_unsupported_deployment_paused_reason_error.py000066400000000000000000000010111523216435200445170ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsSelfHostedResourcesUnsupportedDeploymentPausedReasonError"] class BetaManagedAgentsSelfHostedResourcesUnsupportedDeploymentPausedReasonError(BaseModel): """ The deployment configures resources, but its environment is self-hosted and cannot mount them. """ type: Literal["self_hosted_resources_unsupported_error"] beta_managed_agents_self_hosted_resources_unsupported_run_error.py000066400000000000000000000010411523216435200402360ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsSelfHostedResourcesUnsupportedRunError"] class BetaManagedAgentsSelfHostedResourcesUnsupportedRunError(BaseModel): """ The deployment configures resources, but its environment is self-hosted and cannot mount them. """ message: str """Human-readable error description.""" type: Literal["self_hosted_resources_unsupported_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_session.py000066400000000000000000000040551523216435200306520ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Dict, List, Optional from datetime import datetime from typing_extensions import Literal from ..._models import BaseModel from .beta_managed_agents_session_agent import BetaManagedAgentsSessionAgent from .beta_managed_agents_session_stats import BetaManagedAgentsSessionStats from .beta_managed_agents_session_usage import BetaManagedAgentsSessionUsage from .sessions.beta_managed_agents_session_resource import BetaManagedAgentsSessionResource from .beta_managed_agents_outcome_evaluation_resource import BetaManagedAgentsOutcomeEvaluationResource __all__ = ["BetaManagedAgentsSession"] class BetaManagedAgentsSession(BaseModel): """A Managed Agents `session`.""" id: str agent: BetaManagedAgentsSessionAgent """Resolved `agent` definition for a `session`. Snapshot of the `agent` at `session` creation time. """ archived_at: Optional[datetime] = None """A timestamp in RFC 3339 format""" created_at: datetime """A timestamp in RFC 3339 format""" environment_id: str metadata: Dict[str, str] outcome_evaluations: List[BetaManagedAgentsOutcomeEvaluationResource] """Per-outcome evaluation state. One entry per define_outcome event sent to the session. """ resources: List[BetaManagedAgentsSessionResource] stats: BetaManagedAgentsSessionStats """Timing statistics for a session.""" status: Literal["rescheduling", "running", "idle", "terminated"] """SessionStatus enum""" title: Optional[str] = None type: Literal["session"] updated_at: datetime """A timestamp in RFC 3339 format""" usage: BetaManagedAgentsSessionUsage """Cumulative token usage for a session across all turns.""" vault_ids: List[str] """Vault IDs attached to the session at creation. Empty when no vaults were supplied. """ deployment_id: Optional[str] = None """Deployment ID when the session was created from a deployment reference. Null otherwise. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_session_agent.py000066400000000000000000000037431523216435200320330ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Union, Optional from typing_extensions import Literal, Annotated, TypeAlias from ..._utils import PropertyInfo from ..._models import BaseModel from .beta_managed_agents_custom_tool import BetaManagedAgentsCustomTool from .beta_managed_agents_mcp_toolset import BetaManagedAgentsMCPToolset from .beta_managed_agents_custom_skill import BetaManagedAgentsCustomSkill from .beta_managed_agents_model_config import BetaManagedAgentsModelConfig from .beta_managed_agents_anthropic_skill import BetaManagedAgentsAnthropicSkill from .beta_managed_agents_agent_toolset20260401 import BetaManagedAgentsAgentToolset20260401 from .beta_managed_agents_mcp_server_url_definition import BetaManagedAgentsMCPServerURLDefinition from .beta_managed_agents_session_multiagent_coordinator import BetaManagedAgentsSessionMultiagentCoordinator __all__ = ["BetaManagedAgentsSessionAgent", "Skill", "Tool"] Skill: TypeAlias = Annotated[ Union[BetaManagedAgentsAnthropicSkill, BetaManagedAgentsCustomSkill], PropertyInfo(discriminator="type") ] Tool: TypeAlias = Annotated[ Union[BetaManagedAgentsAgentToolset20260401, BetaManagedAgentsMCPToolset, BetaManagedAgentsCustomTool], PropertyInfo(discriminator="type"), ] class BetaManagedAgentsSessionAgent(BaseModel): """Resolved `agent` definition for a `session`. Snapshot of the `agent` at `session` creation time. """ id: str description: Optional[str] = None mcp_servers: List[BetaManagedAgentsMCPServerURLDefinition] model: BetaManagedAgentsModelConfig """Model identifier and configuration.""" multiagent: Optional[BetaManagedAgentsSessionMultiagentCoordinator] = None """ Resolved coordinator topology with full agent definitions for each roster member. """ name: str skills: List[Skill] system: Optional[str] = None tools: List[Tool] type: Literal["agent"] version: int beta_managed_agents_session_agent_update_param.py000066400000000000000000000027571523216435200345020ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Iterable from typing_extensions import TypeAlias, TypedDict from .beta_managed_agents_custom_tool_params import BetaManagedAgentsCustomToolParams from .beta_managed_agents_mcp_toolset_params import BetaManagedAgentsMCPToolsetParams from .beta_managed_agents_url_mcp_server_params import BetaManagedAgentsURLMCPServerParams from .beta_managed_agents_agent_toolset20260401_params import BetaManagedAgentsAgentToolset20260401Params __all__ = ["BetaManagedAgentsSessionAgentUpdateParam", "Tool"] Tool: TypeAlias = Union[ BetaManagedAgentsAgentToolset20260401Params, BetaManagedAgentsMCPToolsetParams, BetaManagedAgentsCustomToolParams ] class BetaManagedAgentsSessionAgentUpdateParam(TypedDict, total=False): """Mid-session agent configuration update. Only `tools` and `mcp_servers` are updatable. Full replacement: the provided array becomes the new value. To preserve existing entries, GET the session, modify the array, and POST it back. """ mcp_servers: Iterable[BetaManagedAgentsURLMCPServerParams] """Replacement MCP server list. Full replacement: the provided array becomes the new value. Send an empty array to clear; omit to preserve. """ tools: Iterable[Tool] """Replacement tool list. Full replacement: the provided array becomes the new value. Send an empty array to clear; omit to preserve. """ beta_managed_agents_session_creation_rejected_run_error.py000066400000000000000000000007611523216435200364210ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsSessionCreationRejectedRunError"] class BetaManagedAgentsSessionCreationRejectedRunError(BaseModel): """The session create request was rejected with a non-retryable validation error.""" message: str """Human-readable error description.""" type: Literal["session_creation_rejected_error"] beta_managed_agents_session_multiagent_coordinator.py000066400000000000000000000012401523216435200354200ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List from typing_extensions import Literal from ..._models import BaseModel from .beta_managed_agents_session_thread_agent import BetaManagedAgentsSessionThreadAgent __all__ = ["BetaManagedAgentsSessionMultiagentCoordinator"] class BetaManagedAgentsSessionMultiagentCoordinator(BaseModel): """ Resolved coordinator topology with full agent definitions for each roster member. """ agents: List[BetaManagedAgentsSessionThreadAgent] """Full `agent` definitions the coordinator may spawn as session threads.""" type: Literal["coordinator"] beta_managed_agents_session_rate_limited_run_error.py000066400000000000000000000010111523216435200353770ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsSessionRateLimitedRunError"] class BetaManagedAgentsSessionRateLimitedRunError(BaseModel): """Session creation was rejected due to rate limiting. The schedule keeps firing; subsequent runs may succeed. """ message: str """Human-readable error description.""" type: Literal["session_rate_limited_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_session_resource_config.py000066400000000000000000000015111523216435200341000ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from ..._utils import PropertyInfo from .beta_managed_agents_file_resource_config import BetaManagedAgentsFileResourceConfig from .beta_managed_agents_memory_store_resource_config import BetaManagedAgentsMemoryStoreResourceConfig from .beta_managed_agents_github_repository_resource_config import BetaManagedAgentsGitHubRepositoryResourceConfig __all__ = ["BetaManagedAgentsSessionResourceConfig"] BetaManagedAgentsSessionResourceConfig: TypeAlias = Annotated[ Union[ BetaManagedAgentsGitHubRepositoryResourceConfig, BetaManagedAgentsFileResourceConfig, BetaManagedAgentsMemoryStoreResourceConfig, ], PropertyInfo(discriminator="type"), ] beta_managed_agents_session_resource_not_found_deployment_paused_reason_error.py000066400000000000000000000007211523216435200431320ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsSessionResourceNotFoundDeploymentPausedReasonError"] class BetaManagedAgentsSessionResourceNotFoundDeploymentPausedReasonError(BaseModel): """A referenced resource no longer exists and its kind was not reported.""" type: Literal["session_resource_not_found_error"] beta_managed_agents_session_resource_not_found_run_error.py000066400000000000000000000007511523216435200366510ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsSessionResourceNotFoundRunError"] class BetaManagedAgentsSessionResourceNotFoundRunError(BaseModel): """A referenced resource no longer exists and its kind was not reported.""" message: str """Human-readable error description.""" type: Literal["session_resource_not_found_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_session_stats.py000066400000000000000000000011321523216435200320610ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from ..._models import BaseModel __all__ = ["BetaManagedAgentsSessionStats"] class BetaManagedAgentsSessionStats(BaseModel): """Timing statistics for a session.""" active_seconds: Optional[float] = None """Cumulative time in seconds the session spent in running status. Excludes idle time. """ duration_seconds: Optional[float] = None """Elapsed time since session creation in seconds. For terminated sessions, frozen at the final update. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_session_thread_agent.py000066400000000000000000000034321523216435200333550ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Union, Optional from typing_extensions import Literal, Annotated, TypeAlias from ..._utils import PropertyInfo from ..._models import BaseModel from .beta_managed_agents_custom_tool import BetaManagedAgentsCustomTool from .beta_managed_agents_mcp_toolset import BetaManagedAgentsMCPToolset from .beta_managed_agents_custom_skill import BetaManagedAgentsCustomSkill from .beta_managed_agents_model_config import BetaManagedAgentsModelConfig from .beta_managed_agents_anthropic_skill import BetaManagedAgentsAnthropicSkill from .beta_managed_agents_agent_toolset20260401 import BetaManagedAgentsAgentToolset20260401 from .beta_managed_agents_mcp_server_url_definition import BetaManagedAgentsMCPServerURLDefinition __all__ = ["BetaManagedAgentsSessionThreadAgent", "Skill", "Tool"] Skill: TypeAlias = Annotated[ Union[BetaManagedAgentsAnthropicSkill, BetaManagedAgentsCustomSkill], PropertyInfo(discriminator="type") ] Tool: TypeAlias = Annotated[ Union[BetaManagedAgentsAgentToolset20260401, BetaManagedAgentsMCPToolset, BetaManagedAgentsCustomTool], PropertyInfo(discriminator="type"), ] class BetaManagedAgentsSessionThreadAgent(BaseModel): """Resolved `agent` definition for a single `session_thread`. Snapshot of the agent at thread creation time. The multiagent roster is not repeated here; read it from `Session.agent`. """ id: str description: Optional[str] = None mcp_servers: List[BetaManagedAgentsMCPServerURLDefinition] model: BetaManagedAgentsModelConfig """Model identifier and configuration.""" name: str skills: List[Skill] system: Optional[str] = None tools: List[Tool] type: Literal["agent"] version: int anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_session_updated_event.py000066400000000000000000000024241523216435200335570ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Dict, Optional from datetime import datetime from typing_extensions import Literal from ..._models import BaseModel from .beta_managed_agents_session_agent import BetaManagedAgentsSessionAgent __all__ = ["BetaManagedAgentsSessionUpdatedEvent"] class BetaManagedAgentsSessionUpdatedEvent(BaseModel): """Emitted when an UpdateSession request changed at least one field. Carries only the fields that changed; absent fields were not part of the update. The new configuration applies from the next turn. """ id: str """Unique identifier for this event.""" processed_at: datetime """A timestamp in RFC 3339 format""" type: Literal["session.updated"] agent: Optional[BetaManagedAgentsSessionAgent] = None """Resolved `agent` definition for a `session`. Snapshot of the `agent` at `session` creation time. """ metadata: Optional[Dict[str, str]] = None """The session's full metadata bag after the update. Present when the update set non-empty metadata; absent when metadata was unchanged or cleared to empty. """ title: Optional[str] = None """The session's new title. Present only when the update changed it.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_session_usage.py000066400000000000000000000015111523216435200320300ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from ..._models import BaseModel from .beta_managed_agents_cache_creation_usage import BetaManagedAgentsCacheCreationUsage __all__ = ["BetaManagedAgentsSessionUsage"] class BetaManagedAgentsSessionUsage(BaseModel): """Cumulative token usage for a session across all turns.""" cache_creation: Optional[BetaManagedAgentsCacheCreationUsage] = None """Prompt-cache creation token usage broken down by cache lifetime.""" cache_read_input_tokens: Optional[int] = None """Total tokens read from prompt cache.""" input_tokens: Optional[int] = None """Total input tokens consumed across all turns.""" output_tokens: Optional[int] = None """Total output tokens generated across all turns.""" beta_managed_agents_skill_not_found_deployment_paused_reason_error.py000066400000000000000000000006531523216435200406620ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsSkillNotFoundDeploymentPausedReasonError"] class BetaManagedAgentsSkillNotFoundDeploymentPausedReasonError(BaseModel): """A skill referenced by the deployment's agent no longer exists.""" type: Literal["skill_not_found_error"] beta_managed_agents_skill_not_found_run_error.py000066400000000000000000000007031523216435200343720ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsSkillNotFoundRunError"] class BetaManagedAgentsSkillNotFoundRunError(BaseModel): """A skill referenced by the deployment's agent no longer exists.""" message: str """Human-readable error description.""" type: Literal["skill_not_found_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_skill_params.py000066400000000000000000000010411523216435200316400ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from typing_extensions import TypeAlias from .beta_managed_agents_custom_skill_params import BetaManagedAgentsCustomSkillParams from .beta_managed_agents_anthropic_skill_params import BetaManagedAgentsAnthropicSkillParams __all__ = ["BetaManagedAgentsSkillParams"] BetaManagedAgentsSkillParams: TypeAlias = Union[ BetaManagedAgentsAnthropicSkillParams, BetaManagedAgentsCustomSkillParams ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_start_event.py000066400000000000000000000023061523216435200315220ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel from .beta_managed_agents_start_event_preview import BetaManagedAgentsStartEventPreview __all__ = ["BetaManagedAgentsStartEvent"] class BetaManagedAgentsStartEvent(BaseModel): """Opens a preview of a buffered event. Carries the previewed event's type and id only. Followed by zero or more event_delta events with the same event id, normally concluded by the buffered event carrying that id. If the producing model request ends without that event (an error or interrupt mid-stream), its terminal span.model_request_end closes the preview. Only sent on stream connections that opt in via event_deltas; never appears in event history. """ event: BetaManagedAgentsStartEventPreview """The previewed event's type and id. The event type determines which delta types the preview's event_delta events carry: agent.message events stream content_delta fragments; agent.thinking previews are start-only — no deltas follow, and the buffered agent.thinking with the same id concludes them. """ type: Literal["event_start"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_start_event_preview.py000066400000000000000000000011611523216435200332610ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from ..._utils import PropertyInfo from .beta_managed_agents_agent_message_preview import BetaManagedAgentsAgentMessagePreview from .beta_managed_agents_agent_thinking_preview import BetaManagedAgentsAgentThinkingPreview __all__ = ["BetaManagedAgentsStartEventPreview"] BetaManagedAgentsStartEventPreview: TypeAlias = Annotated[ Union[BetaManagedAgentsAgentMessagePreview, BetaManagedAgentsAgentThinkingPreview], PropertyInfo(discriminator="type"), ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_system_content_block.py000066400000000000000000000005601523216435200334140ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsSystemContentBlock"] class BetaManagedAgentsSystemContentBlock(BaseModel): """Regular text content.""" text: str """The text content.""" type: Literal["text"] beta_managed_agents_system_content_block_param.py000066400000000000000000000006621523216435200345200ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsSystemContentBlockParam"] class BetaManagedAgentsSystemContentBlockParam(TypedDict, total=False): """Regular text content.""" text: Required[str] """The text content.""" type: Required[Literal["text"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_system_message_event.py000066400000000000000000000015211523216435200334130ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from datetime import datetime from typing_extensions import Literal from ..._models import BaseModel from .beta_managed_agents_system_content_block import BetaManagedAgentsSystemContentBlock __all__ = ["BetaManagedAgentsSystemMessageEvent"] class BetaManagedAgentsSystemMessageEvent(BaseModel): """A mid-conversation system message event. Carries system-role content that is appended to the session as a `role: "system"` turn. """ id: str """Unique identifier for this event.""" content: List[BetaManagedAgentsSystemContentBlock] """System content blocks. Text-only.""" type: Literal["system.message"] processed_at: Optional[datetime] = None """A timestamp in RFC 3339 format""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_trigger_context.py000066400000000000000000000011641523216435200323740ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from ..._utils import PropertyInfo from .beta_managed_agents_manual_trigger_context import BetaManagedAgentsManualTriggerContext from .beta_managed_agents_schedule_trigger_context import BetaManagedAgentsScheduleTriggerContext __all__ = ["BetaManagedAgentsTriggerContext"] BetaManagedAgentsTriggerContext: TypeAlias = Annotated[ Union[BetaManagedAgentsScheduleTriggerContext, BetaManagedAgentsManualTriggerContext], PropertyInfo(discriminator="type"), ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_trigger_type.py000066400000000000000000000003751523216435200316740ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal, TypeAlias __all__ = ["BetaManagedAgentsTriggerType"] BetaManagedAgentsTriggerType: TypeAlias = Literal["schedule", "manual"] beta_managed_agents_unknown_deployment_paused_reason_error.py000066400000000000000000000007351523216435200371710ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsUnknownDeploymentPausedReasonError"] class BetaManagedAgentsUnknownDeploymentPausedReasonError(BaseModel): """An unrecognized error auto-paused the deployment. A fallback variant; matches a run whose `error.type` is `unknown_error`. """ type: Literal["unknown_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_unknown_run_error.py000066400000000000000000000010201523216435200327500ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsUnknownRunError"] class BetaManagedAgentsUnknownRunError(BaseModel): """An unknown or unexpected error caused the run to fail. A fallback variant; clients that do not recognize a new error type can match on message alone. """ message: str """Human-readable error description.""" type: Literal["unknown_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_url_mcp_server_params.py000066400000000000000000000011061523216435200335530ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsURLMCPServerParams"] class BetaManagedAgentsURLMCPServerParams(TypedDict, total=False): """URL-based MCP server connection.""" name: Required[str] """Unique name for this server, referenced by mcp_toolset configurations. 1-255 characters. """ type: Required[Literal["url"]] url: Required[str] """Endpoint URL for the MCP server.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_user_tool_result_event.py000066400000000000000000000040641523216435200340010ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Union, Optional from datetime import datetime from typing_extensions import Literal, Annotated, TypeAlias from ..._utils import PropertyInfo from ..._models import BaseModel from .sessions.beta_managed_agents_text_block import BetaManagedAgentsTextBlock from .sessions.beta_managed_agents_image_block import BetaManagedAgentsImageBlock from .sessions.beta_managed_agents_document_block import BetaManagedAgentsDocumentBlock from .sessions.beta_managed_agents_search_result_block import BetaManagedAgentsSearchResultBlock __all__ = ["BetaManagedAgentsUserToolResultEvent", "Content"] Content: TypeAlias = Annotated[ Union[ BetaManagedAgentsTextBlock, BetaManagedAgentsImageBlock, BetaManagedAgentsDocumentBlock, BetaManagedAgentsSearchResultBlock, ], PropertyInfo(discriminator="type"), ] class BetaManagedAgentsUserToolResultEvent(BaseModel): """Event sent by the client providing the result of an agent-toolset tool execution. Only valid on `self_hosted` environments, where sandbox-routed tools are executed by the client rather than the server. """ id: str """Unique identifier for this event.""" tool_use_id: str """ The id of the `agent.tool_use` event this result corresponds to, which can be found in the last `session.status_idle` [event's](https://platform.claude.com/docs/en/api/beta/sessions/events/list#beta_managed_agents_session_requires_action.event_ids) `stop_reason.event_ids` field. """ type: Literal["user.tool_result"] content: Optional[List[Content]] = None """The result content returned by the tool.""" is_error: Optional[bool] = None """Whether the tool execution resulted in an error.""" processed_at: Optional[datetime] = None """A timestamp in RFC 3339 format""" session_thread_id: Optional[str] = None """Routes this result to a subagent thread. Copy from the `agent.tool_use` event's `session_thread_id`. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_managed_agents_vault.py000066400000000000000000000015141523216435200303170ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Dict, Optional from datetime import datetime from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsVault"] class BetaManagedAgentsVault(BaseModel): """A vault that stores credentials for use by agents during sessions.""" id: str """Unique identifier for the vault.""" archived_at: Optional[datetime] = None """A timestamp in RFC 3339 format""" created_at: datetime """A timestamp in RFC 3339 format""" display_name: str """Human-readable name for the vault.""" metadata: Dict[str, str] """Arbitrary key-value metadata attached to the vault.""" type: Literal["vault"] updated_at: datetime """A timestamp in RFC 3339 format""" beta_managed_agents_vault_archived_deployment_paused_reason_error.py000066400000000000000000000006351523216435200404710ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsVaultArchivedDeploymentPausedReasonError"] class BetaManagedAgentsVaultArchivedDeploymentPausedReasonError(BaseModel): """A vault referenced by the deployment is archived.""" type: Literal["vault_archived_error"] beta_managed_agents_vault_archived_run_error.py000066400000000000000000000006651523216435200342100ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsVaultArchivedRunError"] class BetaManagedAgentsVaultArchivedRunError(BaseModel): """A vault referenced by the deployment is archived.""" message: str """Human-readable error description.""" type: Literal["vault_archived_error"] beta_managed_agents_vault_not_found_deployment_paused_reason_error.py000066400000000000000000000006431523216435200406760ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsVaultNotFoundDeploymentPausedReasonError"] class BetaManagedAgentsVaultNotFoundDeploymentPausedReasonError(BaseModel): """A vault referenced by the deployment no longer exists.""" type: Literal["vault_not_found_error"] beta_managed_agents_vault_not_found_run_error.py000066400000000000000000000006731523216435200344150ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsVaultNotFoundRunError"] class BetaManagedAgentsVaultNotFoundRunError(BaseModel): """A vault referenced by the deployment no longer exists.""" message: str """Human-readable error description.""" type: Literal["vault_not_found_error"] beta_managed_agents_workspace_archived_deployment_paused_reason_error.py000066400000000000000000000006401523216435200413300ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsWorkspaceArchivedDeploymentPausedReasonError"] class BetaManagedAgentsWorkspaceArchivedDeploymentPausedReasonError(BaseModel): """The deployment's workspace was archived.""" type: Literal["workspace_archived_error"] beta_managed_agents_workspace_archived_run_error.py000066400000000000000000000006701523216435200350470ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaManagedAgentsWorkspaceArchivedRunError"] class BetaManagedAgentsWorkspaceArchivedRunError(BaseModel): """The deployment's workspace was archived.""" message: str """Human-readable error description.""" type: Literal["workspace_archived_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_mcp_tool_config_param.py000066400000000000000000000005541523216435200304730ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import TypedDict __all__ = ["BetaMCPToolConfigParam"] class BetaMCPToolConfigParam(TypedDict, total=False): """Configuration for a specific tool in an MCP toolset.""" defer_loading: bool enabled: bool anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_mcp_tool_default_config_param.py000066400000000000000000000005701523216435200321750ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import TypedDict __all__ = ["BetaMCPToolDefaultConfigParam"] class BetaMCPToolDefaultConfigParam(TypedDict, total=False): """Default configuration for tools in an MCP toolset.""" defer_loading: bool enabled: bool anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_mcp_tool_result_block.py000066400000000000000000000006671523216435200305430ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Union from typing_extensions import Literal from ..._models import BaseModel from .beta_text_block import BetaTextBlock __all__ = ["BetaMCPToolResultBlock"] class BetaMCPToolResultBlock(BaseModel): content: Union[str, List[BetaTextBlock]] is_error: bool tool_use_id: str type: Literal["mcp_tool_result"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_mcp_tool_use_block.py000066400000000000000000000006741523216435200300170ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Dict from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaMCPToolUseBlock"] class BetaMCPToolUseBlock(BaseModel): id: str input: Dict[str, object] name: str """The name of the MCP tool""" server_name: str """The name of the MCP server""" type: Literal["mcp_tool_use"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_mcp_tool_use_block_param.py000066400000000000000000000013231523216435200311670ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, Optional from typing_extensions import Literal, Required, TypedDict from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaMCPToolUseBlockParam"] class BetaMCPToolUseBlockParam(TypedDict, total=False): id: Required[str] input: Required[Dict[str, object]] name: Required[str] server_name: Required[str] """The name of the MCP server""" type: Required[Literal["mcp_tool_use"]] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_mcp_toolset_param.py000066400000000000000000000023301523216435200276540ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, Optional from typing_extensions import Literal, Required, TypedDict from .beta_mcp_tool_config_param import BetaMCPToolConfigParam from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam from .beta_mcp_tool_default_config_param import BetaMCPToolDefaultConfigParam __all__ = ["BetaMCPToolsetParam"] class BetaMCPToolsetParam(TypedDict, total=False): """Configuration for a group of tools from an MCP server. Allows configuring enabled status and defer_loading for all tools from an MCP server, with optional per-tool overrides. """ mcp_server_name: Required[str] """Name of the MCP server to configure tools for""" type: Required[Literal["mcp_toolset"]] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" configs: Optional[Dict[str, BetaMCPToolConfigParam]] """Configuration overrides for specific tools, keyed by tool name""" default_config: BetaMCPToolDefaultConfigParam """Default configuration applied to all tools from this server""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_memory_tool_20250818_command.py000066400000000000000000000022331523216435200312020ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from ..._utils import PropertyInfo from .beta_memory_tool_20250818_view_command import BetaMemoryTool20250818ViewCommand from .beta_memory_tool_20250818_create_command import BetaMemoryTool20250818CreateCommand from .beta_memory_tool_20250818_delete_command import BetaMemoryTool20250818DeleteCommand from .beta_memory_tool_20250818_insert_command import BetaMemoryTool20250818InsertCommand from .beta_memory_tool_20250818_rename_command import BetaMemoryTool20250818RenameCommand from .beta_memory_tool_20250818_str_replace_command import BetaMemoryTool20250818StrReplaceCommand __all__ = ["BetaMemoryTool20250818Command"] BetaMemoryTool20250818Command: TypeAlias = Annotated[ Union[ BetaMemoryTool20250818ViewCommand, BetaMemoryTool20250818CreateCommand, BetaMemoryTool20250818StrReplaceCommand, BetaMemoryTool20250818InsertCommand, BetaMemoryTool20250818DeleteCommand, BetaMemoryTool20250818RenameCommand, ], PropertyInfo(discriminator="command"), ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_memory_tool_20250818_create_command.py000066400000000000000000000007051523216435200325270ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaMemoryTool20250818CreateCommand"] class BetaMemoryTool20250818CreateCommand(BaseModel): command: Literal["create"] """Command type identifier""" file_text: str """Content to write to the file""" path: str """Path where the file should be created""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_memory_tool_20250818_delete_command.py000066400000000000000000000006141523216435200325250ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaMemoryTool20250818DeleteCommand"] class BetaMemoryTool20250818DeleteCommand(BaseModel): command: Literal["delete"] """Command type identifier""" path: str """Path to the file or directory to delete""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_memory_tool_20250818_insert_command.py000066400000000000000000000010421523216435200325630ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaMemoryTool20250818InsertCommand"] class BetaMemoryTool20250818InsertCommand(BaseModel): command: Literal["insert"] """Command type identifier""" insert_line: int """Line number where text should be inserted""" insert_text: str """Text to insert at the specified line""" path: str """Path to the file where text should be inserted""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_memory_tool_20250818_param.py000066400000000000000000000022531523216435200306660ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Iterable, Optional from typing_extensions import Literal, Required, TypedDict from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaMemoryTool20250818Param"] class BetaMemoryTool20250818Param(TypedDict, total=False): name: Required[Literal["memory"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["memory_20250818"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ input_examples: Iterable[Dict[str, object]] strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_memory_tool_20250818_rename_command.py000066400000000000000000000007161523216435200325350ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaMemoryTool20250818RenameCommand"] class BetaMemoryTool20250818RenameCommand(BaseModel): command: Literal["rename"] """Command type identifier""" new_path: str """New path for the file or directory""" old_path: str """Current path of the file or directory""" beta_memory_tool_20250818_str_replace_command.py000066400000000000000000000010141523216435200335020ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaMemoryTool20250818StrReplaceCommand"] class BetaMemoryTool20250818StrReplaceCommand(BaseModel): command: Literal["str_replace"] """Command type identifier""" new_str: str """Text to replace with""" old_str: str """Text to search for and replace""" path: str """Path to the file where text should be replaced""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_memory_tool_20250818_view_command.py000066400000000000000000000010071523216435200322320ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaMemoryTool20250818ViewCommand"] class BetaMemoryTool20250818ViewCommand(BaseModel): command: Literal["view"] """Command type identifier""" path: str """Path to directory or file to view""" view_range: Optional[List[int]] = None """Optional line range for viewing specific lines""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_message.py000066400000000000000000000107101523216435200255710ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from typing_extensions import Literal from ..model import Model from ..._models import BaseModel from .beta_usage import BetaUsage from .beta_container import BetaContainer from .beta_diagnostics import BetaDiagnostics from .beta_stop_reason import BetaStopReason from .beta_content_block import BetaContentBlock, BetaContentBlock as BetaContentBlock from .beta_refusal_stop_details import BetaRefusalStopDetails from .beta_context_management_response import BetaContextManagementResponse __all__ = ["BetaMessage"] class BetaMessage(BaseModel): id: str """Unique object identifier. The format and length of IDs may change over time. """ container: Optional[BetaContainer] = None """ Information about the container used in the request (for the code execution tool) """ content: List[BetaContentBlock] """Content generated by the model. This is an array of content blocks, each of which has a `type` that determines its shape. Example: ```json [{ "type": "text", "text": "Hi, I'm Claude." }] ``` If the request input `messages` ended with an `assistant` turn, then the response `content` will continue directly from that last turn. You can use this to constrain the model's output. For example, if the input `messages` were: ```json [ { "role": "user", "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" }, { "role": "assistant", "content": "The best answer is (" } ] ``` Then the response `content` might be: ```json [{ "type": "text", "text": "B)" }] ``` """ context_management: Optional[BetaContextManagementResponse] = None """Context management response. Information about context management strategies applied during the request. """ diagnostics: Optional[BetaDiagnostics] = None """Response envelope for request-level diagnostics. Present (possibly null) whenever the caller supplied `diagnostics` on the request. """ model: Model """The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. """ role: Literal["assistant"] """Conversational role of the generated message. This will always be `"assistant"`. """ stop_details: Optional[BetaRefusalStopDetails] = None """Structured information about a refusal.""" stop_reason: Optional[BetaStopReason] = None """The reason that we stopped. This may be one the following values: - `"end_turn"`: the model reached a natural stopping point - `"max_tokens"`: we exceeded the requested `max_tokens` or the model's maximum - `"stop_sequence"`: one of your provided custom `stop_sequences` was generated - `"tool_use"`: the model invoked one or more tools - `"pause_turn"`: we paused a long-running turn. You may provide the response back as-is in a subsequent request to let the model continue. - `"refusal"`: when streaming classifiers intervene to handle potential policy violations - `"model_context_window_exceeded"`: we exceeded the model's context window In non-streaming mode this value is always non-null. In streaming mode, it is null in the `message_start` event and non-null otherwise. """ stop_sequence: Optional[str] = None """Which custom stop sequence was generated, if any. This value will be a non-null string if one of your custom stop sequences was generated. """ type: Literal["message"] """Object type. For Messages, this is always `"message"`. """ usage: BetaUsage """Billing and rate-limit usage. Anthropic's API bills and rate-limits by token counts, as tokens represent the underlying cost to our systems. Under the hood, the API transforms requests into a format suitable for the model. The model's output then goes through a parsing stage before becoming an API response. As a result, the token counts in `usage` will not match one-to-one with the exact visible content of an API request or response. For example, `output_tokens` will be non-zero, even for an empty string response from Claude. Total input tokens in a request is the summation of `input_tokens`, `cache_creation_input_tokens`, and `cache_read_input_tokens`. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_message_delta_usage.py000066400000000000000000000040061523216435200301270ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from ..._models import BaseModel from .beta_iterations_usage import BetaIterationsUsage from .beta_server_tool_usage import BetaServerToolUsage from .beta_fallback_credit_usage import BetaFallbackCreditUsage from .beta_output_tokens_details import BetaOutputTokensDetails __all__ = ["BetaMessageDeltaUsage"] class BetaMessageDeltaUsage(BaseModel): cache_creation_input_tokens: Optional[int] = None """The cumulative number of input tokens used to create the cache entry.""" cache_read_input_tokens: Optional[int] = None """The cumulative number of input tokens read from the cache.""" fallback_credit: Optional[BetaFallbackCreditUsage] = None """Outcome of the `fallback_credit_token` presented on this request.""" input_tokens: Optional[int] = None """The cumulative number of input tokens which were used.""" iterations: Optional[BetaIterationsUsage] = None """Per-iteration token usage breakdown. Each entry represents one sampling iteration, with its own input/output token counts and cache statistics. This allows you to: - Determine which iterations exceeded long context thresholds (>=200k tokens) - Calculate the true context window size from the last iteration - Understand token accumulation across server-side tool use loops """ output_tokens: int """The cumulative number of output tokens which were used.""" output_tokens_details: Optional[BetaOutputTokensDetails] = None """Breakdown of output tokens by category. `output_tokens` remains the inclusive, authoritative total used for billing. This object provides a read-only decomposition for observability — for example, how many of the billed output tokens were spent on internal reasoning that may have been summarized before being returned to you. """ server_tool_use: Optional[BetaServerToolUsage] = None """The number of server tool requests.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_message_iteration_usage.py000066400000000000000000000021261523216435200310350ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ..model import Model from ..._models import BaseModel from .beta_cache_creation import BetaCacheCreation __all__ = ["BetaMessageIterationUsage"] class BetaMessageIterationUsage(BaseModel): """Token usage for a sampling iteration.""" cache_creation: Optional[BetaCacheCreation] = None """Breakdown of cached tokens by TTL""" cache_creation_input_tokens: int """The number of input tokens used to create the cache entry.""" cache_read_input_tokens: int """The number of input tokens read from the cache.""" input_tokens: int """The number of input tokens which were used.""" model: Model """The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. """ output_tokens: int """The number of output tokens which were used.""" type: Literal["message"] """Usage for a sampling iteration""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_message_param.py000066400000000000000000000007471523216435200267620ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Iterable from typing_extensions import Literal, Required, TypedDict from .beta_content_block_param import BetaContentBlockParam __all__ = ["BetaMessageParam"] class BetaMessageParam(TypedDict, total=False): content: Required[Union[str, Iterable[BetaContentBlockParam]]] role: Required[Literal["user", "assistant", "system"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_message_tokens_count.py000066400000000000000000000011551523216435200303670ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from ..._models import BaseModel from .beta_count_tokens_context_management_response import BetaCountTokensContextManagementResponse __all__ = ["BetaMessageTokensCount"] class BetaMessageTokensCount(BaseModel): context_management: Optional[BetaCountTokensContextManagementResponse] = None """Information about context management applied to the message.""" input_tokens: int """ The total number of tokens across the provided list of messages, system prompt, and tools. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_metadata_param.py000066400000000000000000000011321523216435200271030ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import TypedDict __all__ = ["BetaMetadataParam"] class BetaMetadataParam(TypedDict, total=False): user_id: Optional[str] """An external identifier for the user who is associated with the request. This should be a uuid, hash value, or other opaque identifier. Anthropic may use this id to help detect abuse. Do not include any identifying information such as name, email address, or phone number. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_mid_conversation_system_block_param.py000066400000000000000000000024021523216435200334450ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .beta_text_block_param import BetaTextBlockParam from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam from .beta_request_tool_removal_block_param import BetaRequestToolRemovalBlockParam from .beta_request_tool_addition_block_param import BetaRequestToolAdditionBlockParam __all__ = ["BetaMidConversationSystemBlockParam", "Content"] Content: TypeAlias = Union[BetaTextBlockParam, BetaRequestToolAdditionBlockParam, BetaRequestToolRemovalBlockParam] class BetaMidConversationSystemBlockParam(TypedDict, total=False): """System instructions that appear mid-conversation. Use this block to provide or update system-level instructions at a specific point in the conversation, rather than only via the top-level `system` parameter. """ content: Required[Iterable[Content]] """System instruction text blocks.""" type: Required[Literal["mid_conv_system"]] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_model_capabilities.py000066400000000000000000000026301523216435200277600ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from ..._models import BaseModel from .beta_effort_capability import BetaEffortCapability from .beta_capability_support import BetaCapabilitySupport from .beta_thinking_capability import BetaThinkingCapability from .beta_context_management_capability import BetaContextManagementCapability __all__ = ["BetaModelCapabilities"] class BetaModelCapabilities(BaseModel): """Model capability information.""" batch: BetaCapabilitySupport """Whether the model supports the Batch API.""" citations: BetaCapabilitySupport """Whether the model supports citation generation.""" code_execution: BetaCapabilitySupport """Whether the model supports code execution tools.""" context_management: BetaContextManagementCapability """Context management support and available strategies.""" effort: BetaEffortCapability """Effort (reasoning_effort) support and available levels.""" image_input: BetaCapabilitySupport """Whether the model accepts image content blocks.""" pdf_input: BetaCapabilitySupport """Whether the model accepts PDF content blocks.""" structured_outputs: BetaCapabilitySupport """Whether the model supports structured output / JSON mode / strict tool schemas.""" thinking: BetaThinkingCapability """Thinking capability and supported type configurations.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_model_info.py000066400000000000000000000024511523216435200262630ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from datetime import datetime from typing_extensions import Literal from ..._models import BaseModel from .beta_model_capabilities import BetaModelCapabilities __all__ = ["BetaModelInfo"] class BetaModelInfo(BaseModel): id: str """Unique model identifier.""" allowed_fallback_models: Optional[List[str]] = None """Model IDs this model accepts as `fallbacks[i].model` on the Messages API. An empty list means the `fallbacks` parameter is not supported for this model as primary. """ capabilities: Optional[BetaModelCapabilities] = None """Model capability information.""" created_at: datetime """RFC 3339 datetime string representing the time at which the model was released. May be set to an epoch value if the release date is unknown. """ display_name: str """A human-readable name for the model.""" max_input_tokens: Optional[int] = None """Maximum input context window size in tokens for this model.""" max_tokens: Optional[int] = None """Maximum value for the `max_tokens` parameter when using this model.""" type: Literal["model"] """Object type. For Models, this is always `"model"`. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_output_config_param.py000066400000000000000000000015641523216435200302210ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, TypedDict from .beta_token_task_budget_param import BetaTokenTaskBudgetParam from .beta_json_output_format_param import BetaJSONOutputFormatParam __all__ = ["BetaOutputConfigParam"] class BetaOutputConfigParam(TypedDict, total=False): effort: Optional[Literal["low", "medium", "high", "xhigh", "max"]] """All possible effort levels.""" format: Optional[BetaJSONOutputFormatParam] """A schema to specify Claude's output format in responses. See [structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs) """ task_budget: Optional[BetaTokenTaskBudgetParam] """User-configurable total token budget across contexts.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_output_tokens_details.py000066400000000000000000000013621523216435200306000ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from ..._models import BaseModel __all__ = ["BetaOutputTokensDetails"] class BetaOutputTokensDetails(BaseModel): thinking_tokens: int """ Number of output tokens the model generated as internal reasoning, including the thinking-block delimiter tokens. Reflects the raw reasoning the model produced, not the (possibly shorter) summarized thinking text returned in the response body. Computed by re-tokenizing the raw reasoning text, so it may differ from the model's exact generation count by a small number of tokens. Always ≤ `output_tokens`; `output_tokens - thinking_tokens` approximates the non-reasoning output. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_packages.py000066400000000000000000000013611523216435200257250ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaPackages"] class BetaPackages(BaseModel): """Packages (and their versions) available in this environment.""" apt: List[str] """Ubuntu/Debian packages to install""" cargo: List[str] """Rust packages to install""" gem: List[str] """Ruby packages to install""" go: List[str] """Go packages to install""" npm: List[str] """Node.js packages to install""" pip: List[str] """Python packages to install""" type: Optional[Literal["packages"]] = None """Package configuration type""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_packages_params.py000066400000000000000000000022201523216435200272630ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, TypedDict from ..._types import SequenceNotStr __all__ = ["BetaPackagesParams"] class BetaPackagesParams(TypedDict, total=False): """Specify packages (and optionally their versions) available in this environment. When versioning, use the version semantics relevant for the package manager, e.g. for `pip` use `package==1.0.0`. You are responsible for validating the package and version exist. Unversioned installs the latest. """ apt: Optional[SequenceNotStr[str]] """Ubuntu/Debian packages to install""" cargo: Optional[SequenceNotStr[str]] """Rust packages to install""" gem: Optional[SequenceNotStr[str]] """Ruby packages to install""" go: Optional[SequenceNotStr[str]] """Go packages to install""" npm: Optional[SequenceNotStr[str]] """Node.js packages to install""" pip: Optional[SequenceNotStr[str]] """Python packages to install""" type: Literal["packages"] """Package configuration type""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_plain_text_source.py000066400000000000000000000004721523216435200277000ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaPlainTextSource"] class BetaPlainTextSource(BaseModel): data: str media_type: Literal["text/plain"] type: Literal["text"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_plain_text_source_param.py000066400000000000000000000006061523216435200310570ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaPlainTextSourceParam"] class BetaPlainTextSourceParam(TypedDict, total=False): data: Required[str] media_type: Required[Literal["text/plain"]] type: Required[Literal["text"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_raw_content_block_delta.py000066400000000000000000000015331523216435200310160ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from ..._utils import PropertyInfo from .beta_text_delta import BetaTextDelta from .beta_thinking_delta import BetaThinkingDelta from .beta_citations_delta import BetaCitationsDelta from .beta_signature_delta import BetaSignatureDelta from .beta_input_json_delta import BetaInputJSONDelta from .beta_compaction_content_block_delta import BetaCompactionContentBlockDelta __all__ = ["BetaRawContentBlockDelta"] BetaRawContentBlockDelta: TypeAlias = Annotated[ Union[ BetaTextDelta, BetaInputJSONDelta, BetaCitationsDelta, BetaThinkingDelta, BetaSignatureDelta, BetaCompactionContentBlockDelta, ], PropertyInfo(discriminator="type"), ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_raw_content_block_delta_event.py000066400000000000000000000006371523216435200322230ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel from .beta_raw_content_block_delta import BetaRawContentBlockDelta __all__ = ["BetaRawContentBlockDeltaEvent"] class BetaRawContentBlockDeltaEvent(BaseModel): delta: BetaRawContentBlockDelta index: int type: Literal["content_block_delta"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_raw_content_block_start_event.py000066400000000000000000000044401523216435200322630ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Literal, Annotated, TypeAlias from ..._utils import PropertyInfo from ..._models import BaseModel from .beta_text_block import BetaTextBlock from .beta_fallback_block import BetaFallbackBlock from .beta_thinking_block import BetaThinkingBlock from .beta_tool_use_block import BetaToolUseBlock from .beta_compaction_block import BetaCompactionBlock from .beta_mcp_tool_use_block import BetaMCPToolUseBlock from .beta_mcp_tool_result_block import BetaMCPToolResultBlock from .beta_server_tool_use_block import BetaServerToolUseBlock from .beta_container_upload_block import BetaContainerUploadBlock from .beta_redacted_thinking_block import BetaRedactedThinkingBlock from .beta_advisor_tool_result_block import BetaAdvisorToolResultBlock from .beta_web_fetch_tool_result_block import BetaWebFetchToolResultBlock from .beta_web_search_tool_result_block import BetaWebSearchToolResultBlock from .beta_tool_search_tool_result_block import BetaToolSearchToolResultBlock from .beta_code_execution_tool_result_block import BetaCodeExecutionToolResultBlock from .beta_bash_code_execution_tool_result_block import BetaBashCodeExecutionToolResultBlock from .beta_text_editor_code_execution_tool_result_block import BetaTextEditorCodeExecutionToolResultBlock __all__ = ["BetaRawContentBlockStartEvent", "ContentBlock"] ContentBlock: TypeAlias = Annotated[ Union[ BetaTextBlock, BetaThinkingBlock, BetaRedactedThinkingBlock, BetaToolUseBlock, BetaServerToolUseBlock, BetaWebSearchToolResultBlock, BetaWebFetchToolResultBlock, BetaAdvisorToolResultBlock, BetaCodeExecutionToolResultBlock, BetaBashCodeExecutionToolResultBlock, BetaTextEditorCodeExecutionToolResultBlock, BetaToolSearchToolResultBlock, BetaMCPToolUseBlock, BetaMCPToolResultBlock, BetaContainerUploadBlock, BetaCompactionBlock, BetaFallbackBlock, ], PropertyInfo(discriminator="type"), ] class BetaRawContentBlockStartEvent(BaseModel): content_block: ContentBlock """Response model for a file uploaded to the container.""" index: int type: Literal["content_block_start"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_raw_content_block_stop_event.py000066400000000000000000000004641523216435200321150ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaRawContentBlockStopEvent"] class BetaRawContentBlockStopEvent(BaseModel): index: int type: Literal["content_block_stop"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_raw_message_delta_event.py000066400000000000000000000035651523216435200310260ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ..._models import BaseModel from .beta_container import BetaContainer from .beta_stop_reason import BetaStopReason from .beta_message_delta_usage import BetaMessageDeltaUsage from .beta_refusal_stop_details import BetaRefusalStopDetails from .beta_context_management_response import BetaContextManagementResponse __all__ = ["BetaRawMessageDeltaEvent", "Delta"] class Delta(BaseModel): container: Optional[BetaContainer] = None """ Information about the container used in the request (for the code execution tool) """ stop_details: Optional[BetaRefusalStopDetails] = None """Structured information about a refusal.""" stop_reason: Optional[BetaStopReason] = None stop_sequence: Optional[str] = None class BetaRawMessageDeltaEvent(BaseModel): context_management: Optional[BetaContextManagementResponse] = None """Information about context management strategies applied during the request""" delta: Delta type: Literal["message_delta"] usage: BetaMessageDeltaUsage """Billing and rate-limit usage. Anthropic's API bills and rate-limits by token counts, as tokens represent the underlying cost to our systems. Under the hood, the API transforms requests into a format suitable for the model. The model's output then goes through a parsing stage before becoming an API response. As a result, the token counts in `usage` will not match one-to-one with the exact visible content of an API request or response. For example, `output_tokens` will be non-zero, even for an empty string response from Claude. Total input tokens in a request is the summation of `input_tokens`, `cache_creation_input_tokens`, and `cache_read_input_tokens`. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_raw_message_start_event.py000066400000000000000000000005271523216435200310650ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel from .beta_message import BetaMessage __all__ = ["BetaRawMessageStartEvent"] class BetaRawMessageStartEvent(BaseModel): message: BetaMessage type: Literal["message_start"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_raw_message_stop_event.py000066400000000000000000000004241523216435200307110ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaRawMessageStopEvent"] class BetaRawMessageStopEvent(BaseModel): type: Literal["message_stop"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_raw_message_stream_event.py000066400000000000000000000017471523216435200312300ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from ..._utils import PropertyInfo from .beta_raw_message_stop_event import BetaRawMessageStopEvent from .beta_raw_message_delta_event import BetaRawMessageDeltaEvent from .beta_raw_message_start_event import BetaRawMessageStartEvent from .beta_raw_content_block_stop_event import BetaRawContentBlockStopEvent from .beta_raw_content_block_delta_event import BetaRawContentBlockDeltaEvent from .beta_raw_content_block_start_event import BetaRawContentBlockStartEvent __all__ = ["BetaRawMessageStreamEvent"] BetaRawMessageStreamEvent: TypeAlias = Annotated[ Union[ BetaRawMessageStartEvent, BetaRawMessageDeltaEvent, BetaRawMessageStopEvent, BetaRawContentBlockStartEvent, BetaRawContentBlockDeltaEvent, BetaRawContentBlockStopEvent, ], PropertyInfo(discriminator="type"), ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_redacted_thinking_block.py000066400000000000000000000004541523216435200307710ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaRedactedThinkingBlock"] class BetaRedactedThinkingBlock(BaseModel): data: str type: Literal["redacted_thinking"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_redacted_thinking_block_param.py000066400000000000000000000005561523216435200321540ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaRedactedThinkingBlockParam"] class BetaRedactedThinkingBlockParam(TypedDict, total=False): data: Required[str] type: Required[Literal["redacted_thinking"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_refusal_stop_details.py000066400000000000000000000112241523216435200303610ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaRefusalStopDetails"] class BetaRefusalStopDetails(BaseModel): """Structured information about a refusal.""" category: Optional[Literal["cyber", "bio", "frontier_llm", "reasoning_extraction", "general_harms"]] = None """The policy category that triggered a refusal. - `cyber` - The request could enable cyber harm, such as malware or exploit development. Benign cybersecurity work can also trigger this category. - `bio` - The request could enable biological harm, such as dangerous lab methods. Beneficial life sciences work can also trigger this category. - `frontier_llm` - The request could assist the development of competing AI models, which is restricted under [Anthropic's commercial terms](https://www.anthropic.com/legal/commercial-terms). Benign machine learning work can also trigger this category. - `reasoning_extraction` - The request asks the model to reproduce its internal reasoning in the response text. To get reasoning in a structured form instead, use [adaptive thinking](https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking). - `general_harms` - The request could be related to an area that was determined as harmful. Benign work might sometimes trigger this category. """ explanation: Optional[str] = None """Human-readable explanation of the refusal. This text is not guaranteed to be stable. `null` when no explanation is available for the category. """ fallback_credit_token: Optional[str] = None """ Opaque code that refunds the cache-miss cost when retrying this refused request on the fallback model. Pass it as `fallback_credit_token` on the retry request. Expires 5 minutes after the refusal. The retry is sent either with the same request body (`system`, `messages`, `tools`, and other render-shaping fields), or with the same body plus one appended `assistant` message whose content is the partial text (with any trailing whitespace stripped from the final text block) and paired server-tool blocks from this refusal — which also authorizes that appended turn as an assistant-prefill continuation on models that otherwise disallow prefill. A token minted mid-server-tool-loop whose partial content was continuable may only be redeemed the second way — if a same-body retry is rejected with a 400 saying the token must be redeemed by continuing the partial response, retry the second way instead. Either way: same workspace, same platform; a mismatch is a 400. Resending a token for an already-warm prefix is permitted but yields no additional credit. `null` when the refused model isn't eligible for a fallback credit. """ fallback_has_prefill_claim: Optional[bool] = None """ Whether the accompanying `fallback_credit_token` may be redeemed with the appended-assistant retry form. Only set when `fallback_credit_token` is present. `true`: retry by resending the same request body plus one appended `assistant` message whose content is this response's `content` with any trailing whitespace stripped from the final text block and unpaired `tool_use` blocks omitted (the same appended-turn shape described on `fallback_credit_token`), with the token attached. `false`: retry by resending the original request body unchanged, with the token attached — the appended-assistant form is not available for this refusal (no continuable partial content, or the request uses `output_format` or a `tool_choice` that forces tool use). One exception: when the request used `output_format` or a forced `tool_choice` and the refusal arrived after server tools (including MCP connector tools) had already executed, the token may not be redeemable by either retry form; if the exact-body retry is then rejected with a 400 saying the token must be redeemed by continuing the partial response, discard the token and retry without it. Advisory: if an appended-assistant retry is rejected with a 400 despite `true`, fall back to resending the original request body with the token. """ recommended_model: Optional[str] = None """The server's suggested retry target for this refusal. Populated when a fallback attempt could not be made (the fallback model's rate limit was exhausted, or it was overloaded); names the fallback model the caller can retry directly. Null otherwise. """ type: Literal["refusal"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_request_document_block_param.py000066400000000000000000000024471523216435200320750ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .beta_url_pdf_source_param import BetaURLPDFSourceParam from .beta_citations_config_param import BetaCitationsConfigParam from .beta_base64_pdf_source_param import BetaBase64PDFSourceParam from .beta_plain_text_source_param import BetaPlainTextSourceParam from .beta_content_block_source_param import BetaContentBlockSourceParam from .beta_file_document_source_param import BetaFileDocumentSourceParam from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaRequestDocumentBlockParam", "Source"] Source: TypeAlias = Union[ BetaBase64PDFSourceParam, BetaPlainTextSourceParam, BetaContentBlockSourceParam, BetaURLPDFSourceParam, BetaFileDocumentSourceParam, ] class BetaRequestDocumentBlockParam(TypedDict, total=False): source: Required[Source] type: Required[Literal["document"]] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" citations: Optional[BetaCitationsConfigParam] context: Optional[str] title: Optional[str] beta_request_mcp_server_tool_configuration_param.py000066400000000000000000000006711523216435200351540ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import TypedDict from ..._types import SequenceNotStr __all__ = ["BetaRequestMCPServerToolConfigurationParam"] class BetaRequestMCPServerToolConfigurationParam(TypedDict, total=False): allowed_tools: Optional[SequenceNotStr[str]] enabled: Optional[bool] beta_request_mcp_server_url_definition_param.py000066400000000000000000000012041523216435200342530ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict from .beta_request_mcp_server_tool_configuration_param import BetaRequestMCPServerToolConfigurationParam __all__ = ["BetaRequestMCPServerURLDefinitionParam"] class BetaRequestMCPServerURLDefinitionParam(TypedDict, total=False): name: Required[str] type: Required[Literal["url"]] url: Required[str] authorization_token: Optional[str] tool_configuration: Optional[BetaRequestMCPServerToolConfigurationParam] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_request_mcp_tool_result_block_param.py000066400000000000000000000013711523216435200334640ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Iterable, Optional from typing_extensions import Literal, Required, TypedDict from .beta_text_block_param import BetaTextBlockParam from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaRequestMCPToolResultBlockParam"] class BetaRequestMCPToolResultBlockParam(TypedDict, total=False): tool_use_id: Required[str] type: Required[Literal["mcp_tool_result"]] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" content: Union[str, Iterable[BetaTextBlockParam]] is_error: bool anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_request_tool_addition_block_param.py000066400000000000000000000030041523216435200330750ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam from .beta_tool_change_tool_reference_param import BetaToolChangeToolReferenceParam from .beta_tool_change_mcp_tool_reference_param import BetaToolChangeMCPToolReferenceParam from .beta_tool_change_mcp_toolset_reference_param import BetaToolChangeMCPToolsetReferenceParam __all__ = ["BetaRequestToolAdditionBlockParam", "Tool"] Tool: TypeAlias = Union[ BetaToolChangeToolReferenceParam, BetaToolChangeMCPToolReferenceParam, BetaToolChangeMCPToolsetReferenceParam ] class BetaRequestToolAdditionBlockParam(TypedDict, total=False): """Mid-conversation directive to surface a declared tool. ``tool`` references a tool (or MCP toolset) by name from the request's ``tools``; it is offered to the model from this point in the conversation onward. """ tool: Required[Tool] """Reference to a single tool the caller declared directly in `tools[]`. Does not accept the composed `{server}_{name}` form the server assigns to MCP-resolved tools — use `mcp_tool_reference` or `mcp_toolset_reference` for those. """ type: Required[Literal["tool_addition"]] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_request_tool_removal_block_param.py000066400000000000000000000030031523216435200327460ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam from .beta_tool_change_tool_reference_param import BetaToolChangeToolReferenceParam from .beta_tool_change_mcp_tool_reference_param import BetaToolChangeMCPToolReferenceParam from .beta_tool_change_mcp_toolset_reference_param import BetaToolChangeMCPToolsetReferenceParam __all__ = ["BetaRequestToolRemovalBlockParam", "Tool"] Tool: TypeAlias = Union[ BetaToolChangeToolReferenceParam, BetaToolChangeMCPToolReferenceParam, BetaToolChangeMCPToolsetReferenceParam ] class BetaRequestToolRemovalBlockParam(TypedDict, total=False): """Mid-conversation directive to withdraw a tool. ``tool`` references a tool (or MCP toolset) by name from the request's ``tools``; it is no longer offered to the model from this point in the conversation onward. """ tool: Required[Tool] """Reference to a single tool the caller declared directly in `tools[]`. Does not accept the composed `{server}_{name}` form the server assigns to MCP-resolved tools — use `mcp_tool_reference` or `mcp_toolset_reference` for those. """ type: Required[Literal["tool_removal"]] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_search_result_block_param.py000066400000000000000000000015121523216435200313420ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Iterable, Optional from typing_extensions import Literal, Required, TypedDict from .beta_text_block_param import BetaTextBlockParam from .beta_citations_config_param import BetaCitationsConfigParam from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaSearchResultBlockParam"] class BetaSearchResultBlockParam(TypedDict, total=False): content: Required[Iterable[BetaTextBlockParam]] source: Required[str] title: Required[str] type: Required[Literal["search_result"]] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" citations: BetaCitationsConfigParam anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_self_hosted_config.py000066400000000000000000000005371523216435200277770ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaSelfHostedConfig"] class BetaSelfHostedConfig(BaseModel): """Configuration for self-hosted environments.""" type: Literal["self_hosted"] """Environment type""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_self_hosted_config_params.py000066400000000000000000000006511523216435200313370ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaSelfHostedConfigParams"] class BetaSelfHostedConfigParams(TypedDict, total=False): """Request params for `self_hosted` environment configuration.""" type: Required[Literal["self_hosted"]] """Environment type""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_server_tool_caller.py000066400000000000000000000005471523216435200300410ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaServerToolCaller"] class BetaServerToolCaller(BaseModel): """Tool invocation generated by a server-side tool.""" tool_id: str type: Literal["code_execution_20250825"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_server_tool_caller_20260120.py000066400000000000000000000004731523216435200310130ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaServerToolCaller20260120"] class BetaServerToolCaller20260120(BaseModel): tool_id: str type: Literal["code_execution_20260120"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_server_tool_caller_20260120_param.py000066400000000000000000000005751523216435200321760ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaServerToolCaller20260120Param"] class BetaServerToolCaller20260120Param(TypedDict, total=False): tool_id: Required[str] type: Required[Literal["code_execution_20260120"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_server_tool_caller_param.py000066400000000000000000000006511523216435200312150ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaServerToolCallerParam"] class BetaServerToolCallerParam(TypedDict, total=False): """Tool invocation generated by a server-side tool.""" tool_id: Required[str] type: Required[Literal["code_execution_20250825"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_server_tool_usage.py000066400000000000000000000005401523216435200276740ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from ..._models import BaseModel __all__ = ["BetaServerToolUsage"] class BetaServerToolUsage(BaseModel): web_fetch_requests: int """The number of web fetch tool requests.""" web_search_requests: int """The number of web search tool requests.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_server_tool_use_block.py000066400000000000000000000021121523216435200305330ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Dict, Union, Optional from typing_extensions import Literal, Annotated, TypeAlias from ..._utils import PropertyInfo from ..._models import BaseModel from .beta_direct_caller import BetaDirectCaller from .beta_server_tool_caller import BetaServerToolCaller from .beta_server_tool_caller_20260120 import BetaServerToolCaller20260120 __all__ = ["BetaServerToolUseBlock", "Caller"] Caller: TypeAlias = Annotated[ Union[BetaDirectCaller, BetaServerToolCaller, BetaServerToolCaller20260120], PropertyInfo(discriminator="type") ] class BetaServerToolUseBlock(BaseModel): id: str input: Dict[str, object] name: Literal[ "advisor", "web_search", "web_fetch", "code_execution", "bash_code_execution", "text_editor_code_execution", "tool_search_tool_regex", "tool_search_tool_bm25", ] type: Literal["server_tool_use"] caller: Optional[Caller] = None """Tool invocation directly from the model.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_server_tool_use_block_param.py000066400000000000000000000025361523216435200317250ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .beta_direct_caller_param import BetaDirectCallerParam from .beta_server_tool_caller_param import BetaServerToolCallerParam from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam from .beta_server_tool_caller_20260120_param import BetaServerToolCaller20260120Param __all__ = ["BetaServerToolUseBlockParam", "Caller"] Caller: TypeAlias = Union[BetaDirectCallerParam, BetaServerToolCallerParam, BetaServerToolCaller20260120Param] class BetaServerToolUseBlockParam(TypedDict, total=False): id: Required[str] input: Required[Dict[str, object]] name: Required[ Literal[ "advisor", "web_search", "web_fetch", "code_execution", "bash_code_execution", "text_editor_code_execution", "tool_search_tool_regex", "tool_search_tool_bm25", ] ] type: Required[Literal["server_tool_use"]] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" caller: Caller """Tool invocation directly from the model.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_signature_delta.py000066400000000000000000000004411523216435200273170ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaSignatureDelta"] class BetaSignatureDelta(BaseModel): signature: str type: Literal["signature_delta"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_skill.py000066400000000000000000000010121523216435200252560ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaSkill"] class BetaSkill(BaseModel): """A skill that was loaded in a container (response model).""" skill_id: str """Skill ID""" type: Literal["anthropic", "custom"] """Type of skill - either 'anthropic' (built-in) or 'custom' (user-defined)""" version: str """Skill version or 'latest' for most recent version""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_skill_params.py000066400000000000000000000011341523216435200266260ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaSkillParams"] class BetaSkillParams(TypedDict, total=False): """Specification for a skill to be loaded in a container (request model).""" skill_id: Required[str] """Skill ID""" type: Required[Literal["anthropic", "custom"]] """Type of skill - either 'anthropic' (built-in) or 'custom' (user-defined)""" version: str """Skill version or 'latest' for most recent version""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_stop_reason.py000066400000000000000000000005551523216435200265070ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal, TypeAlias __all__ = ["BetaStopReason"] BetaStopReason: TypeAlias = Literal[ "end_turn", "max_tokens", "stop_sequence", "tool_use", "pause_turn", "compaction", "refusal", "model_context_window_exceeded", ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_text_block.py000066400000000000000000000012541523216435200263060ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from typing_extensions import Literal from ..._models import BaseModel from .beta_text_citation import BetaTextCitation __all__ = ["BetaTextBlock"] class BetaTextBlock(BaseModel): citations: Optional[List[BetaTextCitation]] = None """Citations supporting the text block. The type of citation returned will depend on the type of document being cited. Citing a PDF results in `page_location`, plain text results in `char_location`, and content document results in `content_block_location`. """ text: str type: Literal["text"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_text_block_param.py000066400000000000000000000012651523216435200274700ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Iterable, Optional from typing_extensions import Literal, Required, TypedDict from .beta_text_citation_param import BetaTextCitationParam from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaTextBlockParam"] class BetaTextBlockParam(TypedDict, total=False): text: Required[str] type: Required[Literal["text"]] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" citations: Optional[Iterable[BetaTextCitationParam]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_text_citation.py000066400000000000000000000016311523216435200270250ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from ..._utils import PropertyInfo from .beta_citation_char_location import BetaCitationCharLocation from .beta_citation_page_location import BetaCitationPageLocation from .beta_citation_content_block_location import BetaCitationContentBlockLocation from .beta_citation_search_result_location import BetaCitationSearchResultLocation from .beta_citations_web_search_result_location import BetaCitationsWebSearchResultLocation __all__ = ["BetaTextCitation"] BetaTextCitation: TypeAlias = Annotated[ Union[ BetaCitationCharLocation, BetaCitationPageLocation, BetaCitationContentBlockLocation, BetaCitationsWebSearchResultLocation, BetaCitationSearchResultLocation, ], PropertyInfo(discriminator="type"), ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_text_citation_param.py000066400000000000000000000016241523216435200302070ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from typing_extensions import TypeAlias from .beta_citation_char_location_param import BetaCitationCharLocationParam from .beta_citation_page_location_param import BetaCitationPageLocationParam from .beta_citation_content_block_location_param import BetaCitationContentBlockLocationParam from .beta_citation_search_result_location_param import BetaCitationSearchResultLocationParam from .beta_citation_web_search_result_location_param import BetaCitationWebSearchResultLocationParam __all__ = ["BetaTextCitationParam"] BetaTextCitationParam: TypeAlias = Union[ BetaCitationCharLocationParam, BetaCitationPageLocationParam, BetaCitationContentBlockLocationParam, BetaCitationWebSearchResultLocationParam, BetaCitationSearchResultLocationParam, ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_text_delta.py000066400000000000000000000004151523216435200263030ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaTextDelta"] class BetaTextDelta(BaseModel): text: str type: Literal["text_delta"] beta_text_editor_code_execution_create_result_block.py000066400000000000000000000005641523216435200355760ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaTextEditorCodeExecutionCreateResultBlock"] class BetaTextEditorCodeExecutionCreateResultBlock(BaseModel): is_file_update: bool type: Literal["text_editor_code_execution_create_result"] beta_text_editor_code_execution_create_result_block_param.py000066400000000000000000000006661523216435200367610ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaTextEditorCodeExecutionCreateResultBlockParam"] class BetaTextEditorCodeExecutionCreateResultBlockParam(TypedDict, total=False): is_file_update: Required[bool] type: Required[Literal["text_editor_code_execution_create_result"]] beta_text_editor_code_execution_str_replace_result_block.py000066400000000000000000000011041523216435200366250ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaTextEditorCodeExecutionStrReplaceResultBlock"] class BetaTextEditorCodeExecutionStrReplaceResultBlock(BaseModel): lines: Optional[List[str]] = None new_lines: Optional[int] = None new_start: Optional[int] = None old_lines: Optional[int] = None old_start: Optional[int] = None type: Literal["text_editor_code_execution_str_replace_result"] beta_text_editor_code_execution_str_replace_result_block_param.py000066400000000000000000000012031523216435200400050ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict from ..._types import SequenceNotStr __all__ = ["BetaTextEditorCodeExecutionStrReplaceResultBlockParam"] class BetaTextEditorCodeExecutionStrReplaceResultBlockParam(TypedDict, total=False): type: Required[Literal["text_editor_code_execution_str_replace_result"]] lines: Optional[SequenceNotStr[str]] new_lines: Optional[int] new_start: Optional[int] old_lines: Optional[int] old_start: Optional[int] beta_text_editor_code_execution_tool_result_block.py000066400000000000000000000021171523216435200353040ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Literal, TypeAlias from ..._models import BaseModel from .beta_text_editor_code_execution_tool_result_error import BetaTextEditorCodeExecutionToolResultError from .beta_text_editor_code_execution_view_result_block import BetaTextEditorCodeExecutionViewResultBlock from .beta_text_editor_code_execution_create_result_block import BetaTextEditorCodeExecutionCreateResultBlock from .beta_text_editor_code_execution_str_replace_result_block import BetaTextEditorCodeExecutionStrReplaceResultBlock __all__ = ["BetaTextEditorCodeExecutionToolResultBlock", "Content"] Content: TypeAlias = Union[ BetaTextEditorCodeExecutionToolResultError, BetaTextEditorCodeExecutionViewResultBlock, BetaTextEditorCodeExecutionCreateResultBlock, BetaTextEditorCodeExecutionStrReplaceResultBlock, ] class BetaTextEditorCodeExecutionToolResultBlock(BaseModel): content: Content tool_use_id: str type: Literal["text_editor_code_execution_tool_result"] beta_text_editor_code_execution_tool_result_block_param.py000066400000000000000000000026761523216435200364760ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam from .beta_text_editor_code_execution_tool_result_error_param import BetaTextEditorCodeExecutionToolResultErrorParam from .beta_text_editor_code_execution_view_result_block_param import BetaTextEditorCodeExecutionViewResultBlockParam from .beta_text_editor_code_execution_create_result_block_param import BetaTextEditorCodeExecutionCreateResultBlockParam from .beta_text_editor_code_execution_str_replace_result_block_param import ( BetaTextEditorCodeExecutionStrReplaceResultBlockParam, ) __all__ = ["BetaTextEditorCodeExecutionToolResultBlockParam", "Content"] Content: TypeAlias = Union[ BetaTextEditorCodeExecutionToolResultErrorParam, BetaTextEditorCodeExecutionViewResultBlockParam, BetaTextEditorCodeExecutionCreateResultBlockParam, BetaTextEditorCodeExecutionStrReplaceResultBlockParam, ] class BetaTextEditorCodeExecutionToolResultBlockParam(TypedDict, total=False): content: Required[Content] tool_use_id: Required[str] type: Required[Literal["text_editor_code_execution_tool_result"]] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" beta_text_editor_code_execution_tool_result_error.py000066400000000000000000000010551523216435200353430ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaTextEditorCodeExecutionToolResultError"] class BetaTextEditorCodeExecutionToolResultError(BaseModel): error_code: Literal[ "invalid_tool_input", "unavailable", "too_many_requests", "execution_time_exceeded", "file_not_found" ] error_message: Optional[str] = None type: Literal["text_editor_code_execution_tool_result_error"] beta_text_editor_code_execution_tool_result_error_param.py000066400000000000000000000011501523216435200365170ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaTextEditorCodeExecutionToolResultErrorParam"] class BetaTextEditorCodeExecutionToolResultErrorParam(TypedDict, total=False): error_code: Required[ Literal["invalid_tool_input", "unavailable", "too_many_requests", "execution_time_exceeded", "file_not_found"] ] type: Required[Literal["text_editor_code_execution_tool_result_error"]] error_message: Optional[str] beta_text_editor_code_execution_view_result_block.py000066400000000000000000000010441523216435200352770ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaTextEditorCodeExecutionViewResultBlock"] class BetaTextEditorCodeExecutionViewResultBlock(BaseModel): content: str file_type: Literal["text", "image", "pdf"] num_lines: Optional[int] = None start_line: Optional[int] = None total_lines: Optional[int] = None type: Literal["text_editor_code_execution_view_result"] beta_text_editor_code_execution_view_result_block_param.py000066400000000000000000000011331523216435200364560ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaTextEditorCodeExecutionViewResultBlockParam"] class BetaTextEditorCodeExecutionViewResultBlockParam(TypedDict, total=False): content: Required[str] file_type: Required[Literal["text", "image", "pdf"]] type: Required[Literal["text_editor_code_execution_view_result"]] num_lines: Optional[int] start_line: Optional[int] total_lines: Optional[int] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_thinking_block.py000066400000000000000000000004531523216435200271350ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaThinkingBlock"] class BetaThinkingBlock(BaseModel): signature: str thinking: str type: Literal["thinking"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_thinking_block_param.py000066400000000000000000000005671523216435200303230ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaThinkingBlockParam"] class BetaThinkingBlockParam(TypedDict, total=False): signature: Required[str] thinking: Required[str] type: Required[Literal["thinking"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_thinking_capability.py000066400000000000000000000007051523216435200301640ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from ..._models import BaseModel from .beta_thinking_types import BetaThinkingTypes __all__ = ["BetaThinkingCapability"] class BetaThinkingCapability(BaseModel): """Thinking capability details.""" supported: bool """Whether this capability is supported by the model.""" types: BetaThinkingTypes """Supported thinking type configurations.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_thinking_config_adaptive_param.py000066400000000000000000000012641523216435200323460ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaThinkingConfigAdaptiveParam"] class BetaThinkingConfigAdaptiveParam(TypedDict, total=False): type: Required[Literal["adaptive"]] display: Optional[Literal["summarized", "omitted"]] """Controls how thinking content appears in the response. When set to `summarized`, thinking is returned normally. When set to `omitted`, thinking content is redacted but a signature is returned for multi-turn continuity. Defaults to `summarized`. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_thinking_config_disabled_param.py000066400000000000000000000005161523216435200323170ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaThinkingConfigDisabledParam"] class BetaThinkingConfigDisabledParam(TypedDict, total=False): type: Required[Literal["disabled"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_thinking_config_enabled_param.py000066400000000000000000000021101523216435200321320ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaThinkingConfigEnabledParam"] class BetaThinkingConfigEnabledParam(TypedDict, total=False): budget_tokens: Required[int] """Determines how many tokens Claude can use for its internal reasoning process. Larger budgets can enable more thorough analysis for complex problems, improving response quality. Must be ≥1024 and less than `max_tokens`. See [extended thinking](https://platform.claude.com/docs/en/build-with-claude/extended-thinking) for details. """ type: Required[Literal["enabled"]] display: Optional[Literal["summarized", "omitted"]] """Controls how thinking content appears in the response. When set to `summarized`, thinking is returned normally. When set to `omitted`, thinking content is redacted but a signature is returned for multi-turn continuity. Defaults to `summarized`. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_thinking_config_param.py000066400000000000000000000011511523216435200304640ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from typing_extensions import TypeAlias from .beta_thinking_config_enabled_param import BetaThinkingConfigEnabledParam from .beta_thinking_config_adaptive_param import BetaThinkingConfigAdaptiveParam from .beta_thinking_config_disabled_param import BetaThinkingConfigDisabledParam __all__ = ["BetaThinkingConfigParam"] BetaThinkingConfigParam: TypeAlias = Union[ BetaThinkingConfigEnabledParam, BetaThinkingConfigDisabledParam, BetaThinkingConfigAdaptiveParam ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_thinking_delta.py000066400000000000000000000017031523216435200271330ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaThinkingDelta"] class BetaThinkingDelta(BaseModel): estimated_tokens: Optional[int] = None """ Per-frame increment of a coarse, running estimate of the tokens this thinking block has produced so far. Present whenever the `thinking-token-count-2026-05-13` beta is set; `null` unless `thinking.display` resolves to `"omitted"` and a count is due this frame. Sum the increments across `thinking_delta` frames on this block for a progress indicator. Each increment is a non-negative multiple of a fixed quantum and the cadence is rate-limited, so this is a deliberately lossy display hint, not a billable count; `usage.output_tokens` remains authoritative. """ thinking: str type: Literal["thinking_delta"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_thinking_turns_param.py000066400000000000000000000005341523216435200303760ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaThinkingTurnsParam"] class BetaThinkingTurnsParam(TypedDict, total=False): type: Required[Literal["thinking_turns"]] value: Required[int] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_thinking_types.py000066400000000000000000000010031523216435200271770ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from ..._models import BaseModel from .beta_capability_support import BetaCapabilitySupport __all__ = ["BetaThinkingTypes"] class BetaThinkingTypes(BaseModel): """Supported thinking type configurations.""" adaptive: BetaCapabilitySupport """Whether the model supports thinking with type 'adaptive' (auto).""" enabled: BetaCapabilitySupport """Whether the model supports thinking with type 'enabled'.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_token_task_budget_param.py000066400000000000000000000014021523216435200310170ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaTokenTaskBudgetParam"] class BetaTokenTaskBudgetParam(TypedDict, total=False): """User-configurable total token budget across contexts.""" total: Required[int] """Total token budget across all contexts in the session.""" type: Required[Literal["tokens"]] """The budget type. Currently only 'tokens' is supported.""" remaining: Optional[int] """Remaining tokens in the budget. Use this to track usage across contexts when implementing compaction client-side. Defaults to total if not provided. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_bash_20241022_param.py000066400000000000000000000022431523216435200302550ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Iterable, Optional from typing_extensions import Literal, Required, TypedDict from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaToolBash20241022Param"] class BetaToolBash20241022Param(TypedDict, total=False): name: Required[Literal["bash"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["bash_20241022"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ input_examples: Iterable[Dict[str, object]] strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_bash_20250124_param.py000066400000000000000000000022431523216435200302600ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Iterable, Optional from typing_extensions import Literal, Required, TypedDict from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaToolBash20250124Param"] class BetaToolBash20250124Param(TypedDict, total=False): name: Required[Literal["bash"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["bash_20250124"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ input_examples: Iterable[Dict[str, object]] strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_change_mcp_tool_reference_param.py000066400000000000000000000010641523216435200335230ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaToolChangeMCPToolReferenceParam"] class BetaToolChangeMCPToolReferenceParam(TypedDict, total=False): """ Reference to a single MCP tool by its server and remote name — the same ``server_name``/``name`` pair ``mcp_tool_use`` carries. """ name: Required[str] server_name: Required[str] type: Required[Literal["mcp_tool_reference"]] beta_tool_change_mcp_toolset_reference_param.py000066400000000000000000000007171523216435200341640ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaToolChangeMCPToolsetReferenceParam"] class BetaToolChangeMCPToolsetReferenceParam(TypedDict, total=False): """Reference to every tool in the named MCP server's toolset.""" server_name: Required[str] type: Required[Literal["mcp_toolset_reference"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_change_tool_reference_param.py000066400000000000000000000011761523216435200326700ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaToolChangeToolReferenceParam"] class BetaToolChangeToolReferenceParam(TypedDict, total=False): """Reference to a single tool the caller declared directly in ``tools[]``. Does not accept the composed ``{server}_{name}`` form the server assigns to MCP-resolved tools — use ``mcp_tool_reference`` or ``mcp_toolset_reference`` for those. """ name: Required[str] type: Required[Literal["tool_reference"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_choice_any_param.py000066400000000000000000000010401523216435200304570ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaToolChoiceAnyParam"] class BetaToolChoiceAnyParam(TypedDict, total=False): """The model will use any available tools.""" type: Required[Literal["any"]] disable_parallel_tool_use: bool """Whether to disable parallel tool use. Defaults to `false`. If set to `true`, the model will output exactly one tool use. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_choice_auto_param.py000066400000000000000000000010651523216435200306470ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaToolChoiceAutoParam"] class BetaToolChoiceAutoParam(TypedDict, total=False): """The model will automatically decide whether to use tools.""" type: Required[Literal["auto"]] disable_parallel_tool_use: bool """Whether to disable parallel tool use. Defaults to `false`. If set to `true`, the model will output at most one tool use. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_choice_none_param.py000066400000000000000000000005611523216435200306360ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaToolChoiceNoneParam"] class BetaToolChoiceNoneParam(TypedDict, total=False): """The model will not be allowed to use tools.""" type: Required[Literal["none"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_choice_param.py000066400000000000000000000011631523216435200276160ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from typing_extensions import TypeAlias from .beta_tool_choice_any_param import BetaToolChoiceAnyParam from .beta_tool_choice_auto_param import BetaToolChoiceAutoParam from .beta_tool_choice_none_param import BetaToolChoiceNoneParam from .beta_tool_choice_tool_param import BetaToolChoiceToolParam __all__ = ["BetaToolChoiceParam"] BetaToolChoiceParam: TypeAlias = Union[ BetaToolChoiceAutoParam, BetaToolChoiceAnyParam, BetaToolChoiceToolParam, BetaToolChoiceNoneParam ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_choice_tool_param.py000066400000000000000000000011721523216435200306530ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaToolChoiceToolParam"] class BetaToolChoiceToolParam(TypedDict, total=False): """The model will use the specified tool with `tool_choice.name`.""" name: Required[str] """The name of the tool to use.""" type: Required[Literal["tool"]] disable_parallel_tool_use: bool """Whether to disable parallel tool use. Defaults to `false`. If set to `true`, the model will output exactly one tool use. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_computer_use_20241022_param.py000066400000000000000000000027021523216435200320520ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Iterable, Optional from typing_extensions import Literal, Required, TypedDict from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaToolComputerUse20241022Param"] class BetaToolComputerUse20241022Param(TypedDict, total=False): display_height_px: Required[int] """The height of the display in pixels.""" display_width_px: Required[int] """The width of the display in pixels.""" name: Required[Literal["computer"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["computer_20241022"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ display_number: Optional[int] """The X11 display number (e.g. 0, 1) for the display.""" input_examples: Iterable[Dict[str, object]] strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_computer_use_20250124_param.py000066400000000000000000000027021523216435200320550ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Iterable, Optional from typing_extensions import Literal, Required, TypedDict from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaToolComputerUse20250124Param"] class BetaToolComputerUse20250124Param(TypedDict, total=False): display_height_px: Required[int] """The height of the display in pixels.""" display_width_px: Required[int] """The width of the display in pixels.""" name: Required[Literal["computer"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["computer_20250124"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ display_number: Optional[int] """The X11 display number (e.g. 0, 1) for the display.""" input_examples: Iterable[Dict[str, object]] strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_computer_use_20251124_param.py000066400000000000000000000030551523216435200320600ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Iterable, Optional from typing_extensions import Literal, Required, TypedDict from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaToolComputerUse20251124Param"] class BetaToolComputerUse20251124Param(TypedDict, total=False): display_height_px: Required[int] """The height of the display in pixels.""" display_width_px: Required[int] """The width of the display in pixels.""" name: Required[Literal["computer"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["computer_20251124"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ display_number: Optional[int] """The X11 display number (e.g. 0, 1) for the display.""" enable_zoom: bool """Whether to enable an action to take a zoomed-in screenshot of the screen.""" input_examples: Iterable[Dict[str, object]] strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_param.py000066400000000000000000000052271523216435200263110ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from ..._types import SequenceNotStr from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaToolParam", "InputSchema"] class InputSchemaTyped(TypedDict, total=False): """[JSON schema](https://json-schema.org/draft/2020-12) for this tool's input. This defines the shape of the `input` that your tool accepts and that the model will produce. """ type: Required[Literal["object"]] properties: Optional[Dict[str, object]] required: Optional[SequenceNotStr[str]] InputSchema: TypeAlias = Union[InputSchemaTyped, Dict[str, object]] class BetaToolParam(TypedDict, total=False): input_schema: Required[InputSchema] """[JSON schema](https://json-schema.org/draft/2020-12) for this tool's input. This defines the shape of the `input` that your tool accepts and that the model will produce. """ name: Required[str] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ description: str """Description of what this tool does. Tool descriptions should be as detailed as possible. The more information that the model has about what the tool is and how to use it, the better it will perform. You can use natural language descriptions to reinforce important aspects of the tool input JSON schema. """ eager_input_streaming: Optional[bool] """Enable eager input streaming for this tool. When true, tool input parameters will be streamed incrementally as they are generated, and types will be inferred on-the-fly rather than buffering the full JSON output. When false, streaming is disabled for this tool even if the fine-grained-tool-streaming beta is active. When null (default), uses the default behavior based on beta headers. """ input_examples: Iterable[Dict[str, object]] strict: bool """When true, guarantees schema validation on tool names and inputs""" type: Optional[Literal["custom"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_reference_block.py000066400000000000000000000004501523216435200303120ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaToolReferenceBlock"] class BetaToolReferenceBlock(BaseModel): tool_name: str type: Literal["tool_reference"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_reference_block_param.py000066400000000000000000000012431523216435200314730ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaToolReferenceBlockParam"] class BetaToolReferenceBlockParam(TypedDict, total=False): """Tool reference block that can be included in tool_result content.""" tool_name: Required[str] type: Required[Literal["tool_reference"]] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_result_block_param.py000066400000000000000000000022651523216435200310600ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .beta_text_block_param import BetaTextBlockParam from .beta_image_block_param import BetaImageBlockParam from .beta_search_result_block_param import BetaSearchResultBlockParam from .beta_tool_reference_block_param import BetaToolReferenceBlockParam from .beta_request_document_block_param import BetaRequestDocumentBlockParam from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaToolResultBlockParam", "Content"] Content: TypeAlias = Union[ BetaTextBlockParam, BetaImageBlockParam, BetaSearchResultBlockParam, BetaRequestDocumentBlockParam, BetaToolReferenceBlockParam, ] class BetaToolResultBlockParam(TypedDict, total=False): tool_use_id: Required[str] type: Required[Literal["tool_result"]] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" content: Union[str, Iterable[Content]] is_error: bool anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_search_tool_bm25_20251119_param.py000066400000000000000000000022631523216435200325010ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Literal, Required, TypedDict from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaToolSearchToolBm25_20251119Param"] class BetaToolSearchToolBm25_20251119Param(TypedDict, total=False): name: Required[Literal["tool_search_tool_bm25"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["tool_search_tool_bm25_20251119", "tool_search_tool_bm25"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_search_tool_regex_20251119_param.py000066400000000000000000000022661523216435200330510ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Literal, Required, TypedDict from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaToolSearchToolRegex20251119Param"] class BetaToolSearchToolRegex20251119Param(TypedDict, total=False): name: Required[Literal["tool_search_tool_regex"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["tool_search_tool_regex_20251119", "tool_search_tool_regex"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_search_tool_result_block.py000066400000000000000000000012171523216435200322560ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Literal, TypeAlias from ..._models import BaseModel from .beta_tool_search_tool_result_error import BetaToolSearchToolResultError from .beta_tool_search_tool_search_result_block import BetaToolSearchToolSearchResultBlock __all__ = ["BetaToolSearchToolResultBlock", "Content"] Content: TypeAlias = Union[BetaToolSearchToolResultError, BetaToolSearchToolSearchResultBlock] class BetaToolSearchToolResultBlock(BaseModel): content: Content tool_use_id: str type: Literal["tool_search_tool_result"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_search_tool_result_block_param.py000066400000000000000000000017251523216435200334420ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam from .beta_tool_search_tool_result_error_param import BetaToolSearchToolResultErrorParam from .beta_tool_search_tool_search_result_block_param import BetaToolSearchToolSearchResultBlockParam __all__ = ["BetaToolSearchToolResultBlockParam", "Content"] Content: TypeAlias = Union[BetaToolSearchToolResultErrorParam, BetaToolSearchToolSearchResultBlockParam] class BetaToolSearchToolResultBlockParam(TypedDict, total=False): content: Required[Content] tool_use_id: Required[str] type: Required[Literal["tool_search_tool_result"]] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_search_tool_result_error.py000066400000000000000000000007441523216435200323210ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaToolSearchToolResultError"] class BetaToolSearchToolResultError(BaseModel): error_code: Literal["invalid_tool_input", "unavailable", "too_many_requests", "execution_time_exceeded"] error_message: Optional[str] = None type: Literal["tool_search_tool_result_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_search_tool_result_error_param.py000066400000000000000000000010371523216435200334750ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaToolSearchToolResultErrorParam"] class BetaToolSearchToolResultErrorParam(TypedDict, total=False): error_code: Required[Literal["invalid_tool_input", "unavailable", "too_many_requests", "execution_time_exceeded"]] type: Required[Literal["tool_search_tool_result_error"]] error_message: Optional[str] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_search_tool_search_result_block.py000066400000000000000000000007071523216435200336060ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List from typing_extensions import Literal from ..._models import BaseModel from .beta_tool_reference_block import BetaToolReferenceBlock __all__ = ["BetaToolSearchToolSearchResultBlock"] class BetaToolSearchToolSearchResultBlock(BaseModel): tool_references: List[BetaToolReferenceBlock] type: Literal["tool_search_tool_search_result"] beta_tool_search_tool_search_result_block_param.py000066400000000000000000000010421523216435200347000ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Iterable from typing_extensions import Literal, Required, TypedDict from .beta_tool_reference_block_param import BetaToolReferenceBlockParam __all__ = ["BetaToolSearchToolSearchResultBlockParam"] class BetaToolSearchToolSearchResultBlockParam(TypedDict, total=False): tool_references: Required[Iterable[BetaToolReferenceBlockParam]] type: Required[Literal["tool_search_tool_search_result"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_text_editor_20241022_param.py000066400000000000000000000023041523216435200316700ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Iterable, Optional from typing_extensions import Literal, Required, TypedDict from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaToolTextEditor20241022Param"] class BetaToolTextEditor20241022Param(TypedDict, total=False): name: Required[Literal["str_replace_editor"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["text_editor_20241022"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ input_examples: Iterable[Dict[str, object]] strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_text_editor_20250124_param.py000066400000000000000000000023041523216435200316730ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Iterable, Optional from typing_extensions import Literal, Required, TypedDict from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaToolTextEditor20250124Param"] class BetaToolTextEditor20250124Param(TypedDict, total=False): name: Required[Literal["str_replace_editor"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["text_editor_20250124"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ input_examples: Iterable[Dict[str, object]] strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_text_editor_20250429_param.py000066400000000000000000000023151523216435200317050ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Iterable, Optional from typing_extensions import Literal, Required, TypedDict from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaToolTextEditor20250429Param"] class BetaToolTextEditor20250429Param(TypedDict, total=False): name: Required[Literal["str_replace_based_edit_tool"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["text_editor_20250429"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ input_examples: Iterable[Dict[str, object]] strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_text_editor_20250728_param.py000066400000000000000000000025711523216435200317130ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Iterable, Optional from typing_extensions import Literal, Required, TypedDict from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaToolTextEditor20250728Param"] class BetaToolTextEditor20250728Param(TypedDict, total=False): name: Required[Literal["str_replace_based_edit_tool"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["text_editor_20250728"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ input_examples: Iterable[Dict[str, object]] max_characters: Optional[int] """Maximum number of characters to display when viewing a file. If not specified, defaults to displaying the full file. """ strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_union_param.py000066400000000000000000000062561523216435200275240ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from typing_extensions import TypeAlias from .beta_tool_param import BetaToolParam from .beta_mcp_toolset_param import BetaMCPToolsetParam from .beta_tool_bash_20241022_param import BetaToolBash20241022Param from .beta_tool_bash_20250124_param import BetaToolBash20250124Param from .beta_memory_tool_20250818_param import BetaMemoryTool20250818Param from .beta_advisor_tool_20260301_param import BetaAdvisorTool20260301Param from .beta_web_fetch_tool_20250910_param import BetaWebFetchTool20250910Param from .beta_web_fetch_tool_20260209_param import BetaWebFetchTool20260209Param from .beta_web_fetch_tool_20260309_param import BetaWebFetchTool20260309Param from .beta_web_fetch_tool_20260318_param import BetaWebFetchTool20260318Param from .beta_web_search_tool_20250305_param import BetaWebSearchTool20250305Param from .beta_web_search_tool_20260209_param import BetaWebSearchTool20260209Param from .beta_web_search_tool_20260318_param import BetaWebSearchTool20260318Param from .beta_tool_text_editor_20241022_param import BetaToolTextEditor20241022Param from .beta_tool_text_editor_20250124_param import BetaToolTextEditor20250124Param from .beta_tool_text_editor_20250429_param import BetaToolTextEditor20250429Param from .beta_tool_text_editor_20250728_param import BetaToolTextEditor20250728Param from .beta_tool_computer_use_20241022_param import BetaToolComputerUse20241022Param from .beta_tool_computer_use_20250124_param import BetaToolComputerUse20250124Param from .beta_tool_computer_use_20251124_param import BetaToolComputerUse20251124Param from .beta_code_execution_tool_20250522_param import BetaCodeExecutionTool20250522Param from .beta_code_execution_tool_20250825_param import BetaCodeExecutionTool20250825Param from .beta_code_execution_tool_20260120_param import BetaCodeExecutionTool20260120Param from .beta_code_execution_tool_20260521_param import BetaCodeExecutionTool20260521Param from .beta_tool_search_tool_bm25_20251119_param import BetaToolSearchToolBm25_20251119Param from .beta_tool_search_tool_regex_20251119_param import BetaToolSearchToolRegex20251119Param __all__ = ["BetaToolUnionParam"] BetaToolUnionParam: TypeAlias = Union[ BetaToolParam, BetaToolBash20241022Param, BetaToolBash20250124Param, BetaCodeExecutionTool20250522Param, BetaCodeExecutionTool20250825Param, BetaCodeExecutionTool20260120Param, BetaCodeExecutionTool20260521Param, BetaToolComputerUse20241022Param, BetaMemoryTool20250818Param, BetaToolComputerUse20250124Param, BetaToolTextEditor20241022Param, BetaToolComputerUse20251124Param, BetaToolTextEditor20250124Param, BetaToolTextEditor20250429Param, BetaToolTextEditor20250728Param, BetaWebSearchTool20250305Param, BetaWebFetchTool20250910Param, BetaWebSearchTool20260209Param, BetaWebFetchTool20260209Param, BetaWebFetchTool20260309Param, BetaWebSearchTool20260318Param, BetaWebFetchTool20260318Param, BetaAdvisorTool20260301Param, BetaToolSearchToolBm25_20251119Param, BetaToolSearchToolRegex20251119Param, BetaMCPToolsetParam, ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_use_block.py000066400000000000000000000015141523216435200271520ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Dict, Union, Optional from typing_extensions import Literal, Annotated, TypeAlias from ..._utils import PropertyInfo from ..._models import BaseModel from .beta_direct_caller import BetaDirectCaller from .beta_server_tool_caller import BetaServerToolCaller from .beta_server_tool_caller_20260120 import BetaServerToolCaller20260120 __all__ = ["BetaToolUseBlock", "Caller"] Caller: TypeAlias = Annotated[ Union[BetaDirectCaller, BetaServerToolCaller, BetaServerToolCaller20260120], PropertyInfo(discriminator="type") ] class BetaToolUseBlock(BaseModel): id: str input: Dict[str, object] name: str type: Literal["tool_use"] caller: Optional[Caller] = None """Tool invocation directly from the model.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_use_block_param.py000066400000000000000000000020561523216435200303340ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .beta_direct_caller_param import BetaDirectCallerParam from .beta_server_tool_caller_param import BetaServerToolCallerParam from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam from .beta_server_tool_caller_20260120_param import BetaServerToolCaller20260120Param __all__ = ["BetaToolUseBlockParam", "Caller"] Caller: TypeAlias = Union[BetaDirectCallerParam, BetaServerToolCallerParam, BetaServerToolCaller20260120Param] class BetaToolUseBlockParam(TypedDict, total=False): id: Required[str] input: Required[Dict[str, object]] name: Required[str] type: Required[Literal["tool_use"]] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" caller: Caller """Tool invocation directly from the model.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_uses_keep_param.py000066400000000000000000000005251523216435200303500ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaToolUsesKeepParam"] class BetaToolUsesKeepParam(TypedDict, total=False): type: Required[Literal["tool_uses"]] value: Required[int] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tool_uses_trigger_param.py000066400000000000000000000005331523216435200310660ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaToolUsesTriggerParam"] class BetaToolUsesTriggerParam(TypedDict, total=False): type: Required[Literal["tool_uses"]] value: Required[int] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tunnel.py000066400000000000000000000016301523216435200254530ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from datetime import datetime from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaTunnel"] class BetaTunnel(BaseModel): """An MCP tunnel.""" id: str """Unique identifier for the tunnel, prefixed with `tnl_`.""" archived_at: Optional[datetime] = None """A timestamp in RFC 3339 format""" created_at: datetime """A timestamp in RFC 3339 format""" display_name: Optional[str] = None """Human-readable name for the tunnel (1-255 characters). Null if unset.""" domain: str """Anthropic-assigned hostname for the tunnel. MCP server URLs whose host is a subdomain of this value are routed through the tunnel. Globally unique and never reused, even after the tunnel is archived. """ type: Literal["tunnel"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_tunnel_token.py000066400000000000000000000010021523216435200266440ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaTunnelToken"] class BetaTunnelToken(BaseModel): """A tunnel's connector token.""" id: str """Stable identifier for the current token value. Changes when the token is rotated. """ tunnel_token: str """The connector token used to run the tunnel. Treat as a credential.""" type: Literal["tunnel_token"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_unrestricted_network.py000066400000000000000000000005321523216435200304320ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaUnrestrictedNetwork"] class BetaUnrestrictedNetwork(BaseModel): """Unrestricted network access.""" type: Literal["unrestricted"] """Network policy type""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_unrestricted_network_param.py000066400000000000000000000006221523216435200316120ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaUnrestrictedNetworkParam"] class BetaUnrestrictedNetworkParam(TypedDict, total=False): """Unrestricted network access.""" type: Required[Literal["unrestricted"]] """Network policy type""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_url_image_source_param.py000066400000000000000000000005211523216435200306500ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaURLImageSourceParam"] class BetaURLImageSourceParam(TypedDict, total=False): type: Required[Literal["url"]] url: Required[str] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_url_pdf_source_param.py000066400000000000000000000005151523216435200303420ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaURLPDFSourceParam"] class BetaURLPDFSourceParam(TypedDict, total=False): type: Required[Literal["url"]] url: Required[str] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_usage.py000066400000000000000000000052021523216435200252510ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ..._models import BaseModel from .beta_cache_creation import BetaCacheCreation from .beta_iterations_usage import BetaIterationsUsage from .beta_server_tool_usage import BetaServerToolUsage from .beta_fallback_credit_usage import BetaFallbackCreditUsage from .beta_output_tokens_details import BetaOutputTokensDetails __all__ = ["BetaUsage"] class BetaUsage(BaseModel): cache_creation: Optional[BetaCacheCreation] = None """Breakdown of cached tokens by TTL""" cache_creation_input_tokens: Optional[int] = None """The number of input tokens used to create the cache entry.""" cache_read_input_tokens: Optional[int] = None """The number of input tokens read from the cache.""" fallback_credit: Optional[BetaFallbackCreditUsage] = None """Outcome of the `fallback_credit_token` presented on this request.""" inference_geo: Optional[str] = None """The geographic region where inference was performed for this request.""" input_tokens: int """The number of input tokens which were used.""" iterations: Optional[BetaIterationsUsage] = None """Per-iteration token usage breakdown. Each entry represents one sampling iteration, with its own input/output token counts and cache statistics. This allows you to: - Determine which iterations exceeded long context thresholds (>=200k tokens) - Calculate the true context window size from the last iteration - Understand token accumulation across server-side tool use loops """ output_tokens: int """The number of output tokens which were used.""" output_tokens_details: Optional[BetaOutputTokensDetails] = None """Breakdown of output tokens by category. `output_tokens` remains the inclusive, authoritative total used for billing. This object provides a read-only decomposition for observability — for example, how many of the billed output tokens were spent on internal reasoning that may have been summarized before being returned to you. """ server_tool_use: Optional[BetaServerToolUsage] = None """The number of server tool requests.""" service_tier: Optional[Literal["standard", "priority", "batch"]] = None """If the request used the priority, standard, or batch tier.""" speed: Optional[Literal["standard", "fast"]] = None """Inference speed mode. `fast` provides significantly faster output token generation at premium pricing. Not all models support `fast`; invalid combinations are rejected at create time. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_user_location_param.py000066400000000000000000000013201523216435200301700ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaUserLocationParam"] class BetaUserLocationParam(TypedDict, total=False): type: Required[Literal["approximate"]] city: Optional[str] """The city of the user.""" country: Optional[str] """ The two letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the user. """ region: Optional[str] """The region of the user.""" timezone: Optional[str] """The [IANA timezone](https://nodatime.org/TimeZones) of the user.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_user_profile.py000066400000000000000000000030351523216435200266450ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Dict, Optional from datetime import datetime from typing_extensions import Literal from ..._models import BaseModel from .beta_user_profile_trust_grant import BetaUserProfileTrustGrant __all__ = ["BetaUserProfile"] class BetaUserProfile(BaseModel): id: str """Unique identifier for this user profile, prefixed `uprof_`.""" created_at: datetime """A timestamp in RFC 3339 format""" metadata: Dict[str, str] """Arbitrary key-value metadata. Maximum 16 pairs, keys up to 64 chars, values up to 512 chars. """ relationship: Literal["external", "resold", "internal"] """ How the entity behind a user profile relates to the platform that owns the API key. `external`: an individual end-user of the platform. `resold`: a company the platform resells Claude access to. `internal`: the platform's own usage. """ trust_grants: Dict[str, BetaUserProfileTrustGrant] """Trust grants for this profile, keyed by grant name. Key omitted when no grant is active or in flight. """ type: Literal["user_profile"] """Object type. Always `user_profile`.""" updated_at: datetime """A timestamp in RFC 3339 format""" external_id: Optional[str] = None """Platform's own identifier for this user. Not enforced unique.""" name: Optional[str] = None """Display name of the entity this profile represents. For `resold` this is the resold-to company's name. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_user_profile_enrollment_url.py000066400000000000000000000010131523216435200317600ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from datetime import datetime from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaUserProfileEnrollmentURL"] class BetaUserProfileEnrollmentURL(BaseModel): expires_at: datetime """A timestamp in RFC 3339 format""" type: Literal["enrollment_url"] """Object type. Always `enrollment_url`.""" url: str """Enrollment URL to send to the end user. Valid until `expires_at`.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_user_profile_trust_grant.py000066400000000000000000000005201523216435200312750ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaUserProfileTrustGrant"] class BetaUserProfileTrustGrant(BaseModel): status: Literal["active", "pending", "rejected"] """Status of the trust grant.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_web_fetch_block.py000066400000000000000000000010131523216435200272410ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ..._models import BaseModel from .beta_document_block import BetaDocumentBlock __all__ = ["BetaWebFetchBlock"] class BetaWebFetchBlock(BaseModel): content: BetaDocumentBlock retrieved_at: Optional[str] = None """ISO 8601 timestamp when the content was retrieved""" type: Literal["web_fetch_result"] url: str """Fetched content URL""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_web_fetch_block_param.py000066400000000000000000000011671523216435200304330ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict from .beta_request_document_block_param import BetaRequestDocumentBlockParam __all__ = ["BetaWebFetchBlockParam"] class BetaWebFetchBlockParam(TypedDict, total=False): content: Required[BetaRequestDocumentBlockParam] type: Required[Literal["web_fetch_result"]] url: Required[str] """Fetched content URL""" retrieved_at: Optional[str] """ISO 8601 timestamp when the content was retrieved""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_web_fetch_tool_20250910_param.py000066400000000000000000000035761523216435200313060ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Literal, Required, TypedDict from ..._types import SequenceNotStr from .beta_citations_config_param import BetaCitationsConfigParam from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaWebFetchTool20250910Param"] class BetaWebFetchTool20250910Param(TypedDict, total=False): name: Required[Literal["web_fetch"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["web_fetch_20250910"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] allowed_domains: Optional[SequenceNotStr[str]] """List of domains to allow fetching from""" blocked_domains: Optional[SequenceNotStr[str]] """List of domains to block fetching from""" cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" citations: Optional[BetaCitationsConfigParam] """Citations configuration for fetched documents. Citations are disabled by default. """ defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ max_content_tokens: Optional[int] """Maximum number of tokens used by including web page text content in the context. The limit is approximate and does not apply to binary content such as PDFs. """ max_uses: Optional[int] """Maximum number of times the tool can be used in the API request.""" strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_web_fetch_tool_20260209_param.py000066400000000000000000000035761523216435200313100ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Literal, Required, TypedDict from ..._types import SequenceNotStr from .beta_citations_config_param import BetaCitationsConfigParam from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaWebFetchTool20260209Param"] class BetaWebFetchTool20260209Param(TypedDict, total=False): name: Required[Literal["web_fetch"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["web_fetch_20260209"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] allowed_domains: Optional[SequenceNotStr[str]] """List of domains to allow fetching from""" blocked_domains: Optional[SequenceNotStr[str]] """List of domains to block fetching from""" cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" citations: Optional[BetaCitationsConfigParam] """Citations configuration for fetched documents. Citations are disabled by default. """ defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ max_content_tokens: Optional[int] """Maximum number of tokens used by including web page text content in the context. The limit is approximate and does not apply to binary content such as PDFs. """ max_uses: Optional[int] """Maximum number of times the tool can be used in the API request.""" strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_web_fetch_tool_20260309_param.py000066400000000000000000000043061523216435200313010ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Literal, Required, TypedDict from ..._types import SequenceNotStr from .beta_citations_config_param import BetaCitationsConfigParam from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaWebFetchTool20260309Param"] class BetaWebFetchTool20260309Param(TypedDict, total=False): """Web fetch tool with use_cache parameter for bypassing cached content.""" name: Required[Literal["web_fetch"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["web_fetch_20260309"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] allowed_domains: Optional[SequenceNotStr[str]] """List of domains to allow fetching from""" blocked_domains: Optional[SequenceNotStr[str]] """List of domains to block fetching from""" cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" citations: Optional[BetaCitationsConfigParam] """Citations configuration for fetched documents. Citations are disabled by default. """ defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ max_content_tokens: Optional[int] """Maximum number of tokens used by including web page text content in the context. The limit is approximate and does not apply to binary content such as PDFs. """ max_uses: Optional[int] """Maximum number of times the tool can be used in the API request.""" strict: bool """When true, guarantees schema validation on tool names and inputs""" use_cache: bool """Whether to use cached content. Set to false to bypass the cache and fetch fresh content. Only set to false when the user explicitly requests fresh content or when fetching rapidly-changing sources. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_web_fetch_tool_20260318_param.py000066400000000000000000000051661523216435200313060ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Literal, Required, TypedDict from ..._types import SequenceNotStr from .beta_citations_config_param import BetaCitationsConfigParam from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaWebFetchTool20260318Param"] class BetaWebFetchTool20260318Param(TypedDict, total=False): name: Required[Literal["web_fetch"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["web_fetch_20260318"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] allowed_domains: Optional[SequenceNotStr[str]] """List of domains to allow fetching from""" blocked_domains: Optional[SequenceNotStr[str]] """List of domains to block fetching from""" cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" citations: Optional[BetaCitationsConfigParam] """Citations configuration for fetched documents. Citations are disabled by default. """ defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ max_content_tokens: Optional[int] """Maximum number of tokens used by including web page text content in the context. The limit is approximate and does not apply to binary content such as PDFs. """ max_uses: Optional[int] """Maximum number of times the tool can be used in the API request.""" response_inclusion: Literal["full", "excluded"] """ How this tool's result blocks appear in the API response when the result was consumed by a completed code_execution call in the same turn. 'full' returns the complete content (default). 'excluded' drops the nested server_tool_use and result block pair entirely. Results from direct calls, or from code_execution calls that paused before completing, are always returned in full so they can be sent back on the next turn. """ strict: bool """When true, guarantees schema validation on tool names and inputs""" use_cache: bool """Whether to use cached content. Set to false to bypass the cache and fetch fresh content. Only set to false when the user explicitly requests fresh content or when fetching rapidly-changing sources. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_web_fetch_tool_result_block.py000066400000000000000000000021001523216435200316720ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union, Optional from typing_extensions import Literal, Annotated, TypeAlias from ..._utils import PropertyInfo from ..._models import BaseModel from .beta_direct_caller import BetaDirectCaller from .beta_web_fetch_block import BetaWebFetchBlock from .beta_server_tool_caller import BetaServerToolCaller from .beta_server_tool_caller_20260120 import BetaServerToolCaller20260120 from .beta_web_fetch_tool_result_error_block import BetaWebFetchToolResultErrorBlock __all__ = ["BetaWebFetchToolResultBlock", "Content", "Caller"] Content: TypeAlias = Union[BetaWebFetchToolResultErrorBlock, BetaWebFetchBlock] Caller: TypeAlias = Annotated[ Union[BetaDirectCaller, BetaServerToolCaller, BetaServerToolCaller20260120], PropertyInfo(discriminator="type") ] class BetaWebFetchToolResultBlock(BaseModel): content: Content tool_use_id: str type: Literal["web_fetch_tool_result"] caller: Optional[Caller] = None """Tool invocation directly from the model.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_web_fetch_tool_result_block_param.py000066400000000000000000000024701523216435200330640ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .beta_direct_caller_param import BetaDirectCallerParam from .beta_web_fetch_block_param import BetaWebFetchBlockParam from .beta_server_tool_caller_param import BetaServerToolCallerParam from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam from .beta_server_tool_caller_20260120_param import BetaServerToolCaller20260120Param from .beta_web_fetch_tool_result_error_block_param import BetaWebFetchToolResultErrorBlockParam __all__ = ["BetaWebFetchToolResultBlockParam", "Content", "Caller"] Content: TypeAlias = Union[BetaWebFetchToolResultErrorBlockParam, BetaWebFetchBlockParam] Caller: TypeAlias = Union[BetaDirectCallerParam, BetaServerToolCallerParam, BetaServerToolCaller20260120Param] class BetaWebFetchToolResultBlockParam(TypedDict, total=False): content: Required[Content] tool_use_id: Required[str] type: Required[Literal["web_fetch_tool_result"]] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" caller: Caller """Tool invocation directly from the model.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_web_fetch_tool_result_error_block.py000066400000000000000000000006711523216435200331160ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel from .beta_web_fetch_tool_result_error_code import BetaWebFetchToolResultErrorCode __all__ = ["BetaWebFetchToolResultErrorBlock"] class BetaWebFetchToolResultErrorBlock(BaseModel): error_code: BetaWebFetchToolResultErrorCode type: Literal["web_fetch_tool_result_error"] beta_web_fetch_tool_result_error_block_param.py000066400000000000000000000007741523216435200342230ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict from .beta_web_fetch_tool_result_error_code import BetaWebFetchToolResultErrorCode __all__ = ["BetaWebFetchToolResultErrorBlockParam"] class BetaWebFetchToolResultErrorBlockParam(TypedDict, total=False): error_code: Required[BetaWebFetchToolResultErrorCode] type: Required[Literal["web_fetch_tool_result_error"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_web_fetch_tool_result_error_code.py000066400000000000000000000007241523216435200327350ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal, TypeAlias __all__ = ["BetaWebFetchToolResultErrorCode"] BetaWebFetchToolResultErrorCode: TypeAlias = Literal[ "invalid_tool_input", "url_too_long", "url_not_allowed", "url_not_in_prior_context", "url_not_accessible", "unsupported_content_type", "too_many_requests", "max_uses_exceeded", "unavailable", ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_web_search_result_block.py000066400000000000000000000006251523216435200310230ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebSearchResultBlock"] class BetaWebSearchResultBlock(BaseModel): encrypted_content: str page_age: Optional[str] = None title: str type: Literal["web_search_result"] url: str anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_web_search_result_block_param.py000066400000000000000000000007441523216435200322050ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaWebSearchResultBlockParam"] class BetaWebSearchResultBlockParam(TypedDict, total=False): encrypted_content: Required[str] title: Required[str] type: Required[Literal["web_search_result"]] url: Required[str] page_age: Optional[str] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_web_search_tool_20250305_param.py000066400000000000000000000034711523216435200314520ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Literal, Required, TypedDict from ..._types import SequenceNotStr from .beta_user_location_param import BetaUserLocationParam from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaWebSearchTool20250305Param"] class BetaWebSearchTool20250305Param(TypedDict, total=False): name: Required[Literal["web_search"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["web_search_20250305"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] allowed_domains: Optional[SequenceNotStr[str]] """If provided, only these domains will be included in results. Cannot be used alongside `blocked_domains`. """ blocked_domains: Optional[SequenceNotStr[str]] """If provided, these domains will never appear in results. Cannot be used alongside `allowed_domains`. """ cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ max_uses: Optional[int] """Maximum number of times the tool can be used in the API request.""" strict: bool """When true, guarantees schema validation on tool names and inputs""" user_location: Optional[BetaUserLocationParam] """Parameters for the user's location. Used to provide more relevant search results. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_web_search_tool_20260209_param.py000066400000000000000000000034711523216435200314560ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Literal, Required, TypedDict from ..._types import SequenceNotStr from .beta_user_location_param import BetaUserLocationParam from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaWebSearchTool20260209Param"] class BetaWebSearchTool20260209Param(TypedDict, total=False): name: Required[Literal["web_search"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["web_search_20260209"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] allowed_domains: Optional[SequenceNotStr[str]] """If provided, only these domains will be included in results. Cannot be used alongside `blocked_domains`. """ blocked_domains: Optional[SequenceNotStr[str]] """If provided, these domains will never appear in results. Cannot be used alongside `allowed_domains`. """ cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ max_uses: Optional[int] """Maximum number of times the tool can be used in the API request.""" strict: bool """When true, guarantees schema validation on tool names and inputs""" user_location: Optional[BetaUserLocationParam] """Parameters for the user's location. Used to provide more relevant search results. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_web_search_tool_20260318_param.py000066400000000000000000000044721523216435200314610ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Literal, Required, TypedDict from ..._types import SequenceNotStr from .beta_user_location_param import BetaUserLocationParam from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam __all__ = ["BetaWebSearchTool20260318Param"] class BetaWebSearchTool20260318Param(TypedDict, total=False): name: Required[Literal["web_search"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["web_search_20260318"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] allowed_domains: Optional[SequenceNotStr[str]] """If provided, only these domains will be included in results. Cannot be used alongside `blocked_domains`. """ blocked_domains: Optional[SequenceNotStr[str]] """If provided, these domains will never appear in results. Cannot be used alongside `allowed_domains`. """ cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ max_uses: Optional[int] """Maximum number of times the tool can be used in the API request.""" response_inclusion: Literal["full", "excluded"] """ How this tool's result blocks appear in the API response when the result was consumed by a completed code_execution call in the same turn. 'full' returns the complete content (default). 'excluded' drops the nested server_tool_use and result block pair entirely. Results from direct calls, or from code_execution calls that paused before completing, are always returned in full so they can be sent back on the next turn. """ strict: bool """When true, guarantees schema validation on tool names and inputs""" user_location: Optional[BetaUserLocationParam] """Parameters for the user's location. Used to provide more relevant search results. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_web_search_tool_request_error_param.py000066400000000000000000000007721523216435200334540ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict from .beta_web_search_tool_result_error_code import BetaWebSearchToolResultErrorCode __all__ = ["BetaWebSearchToolRequestErrorParam"] class BetaWebSearchToolRequestErrorParam(TypedDict, total=False): error_code: Required[BetaWebSearchToolResultErrorCode] type: Required[Literal["web_search_tool_result_error"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_web_search_tool_result_block.py000066400000000000000000000017251523216435200320620ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union, Optional from typing_extensions import Literal, Annotated, TypeAlias from ..._utils import PropertyInfo from ..._models import BaseModel from .beta_direct_caller import BetaDirectCaller from .beta_server_tool_caller import BetaServerToolCaller from .beta_server_tool_caller_20260120 import BetaServerToolCaller20260120 from .beta_web_search_tool_result_block_content import BetaWebSearchToolResultBlockContent __all__ = ["BetaWebSearchToolResultBlock", "Caller"] Caller: TypeAlias = Annotated[ Union[BetaDirectCaller, BetaServerToolCaller, BetaServerToolCaller20260120], PropertyInfo(discriminator="type") ] class BetaWebSearchToolResultBlock(BaseModel): content: BetaWebSearchToolResultBlockContent tool_use_id: str type: Literal["web_search_tool_result"] caller: Optional[Caller] = None """Tool invocation directly from the model.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_web_search_tool_result_block_content.py000066400000000000000000000007271523216435200336150ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Union from typing_extensions import TypeAlias from .beta_web_search_result_block import BetaWebSearchResultBlock from .beta_web_search_tool_result_error import BetaWebSearchToolResultError __all__ = ["BetaWebSearchToolResultBlockContent"] BetaWebSearchToolResultBlockContent: TypeAlias = Union[BetaWebSearchToolResultError, List[BetaWebSearchResultBlock]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_web_search_tool_result_block_param.py000066400000000000000000000023151523216435200332360ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .beta_direct_caller_param import BetaDirectCallerParam from .beta_server_tool_caller_param import BetaServerToolCallerParam from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam from .beta_server_tool_caller_20260120_param import BetaServerToolCaller20260120Param from .beta_web_search_tool_result_block_param_content_param import BetaWebSearchToolResultBlockParamContentParam __all__ = ["BetaWebSearchToolResultBlockParam", "Caller"] Caller: TypeAlias = Union[BetaDirectCallerParam, BetaServerToolCallerParam, BetaServerToolCaller20260120Param] class BetaWebSearchToolResultBlockParam(TypedDict, total=False): content: Required[BetaWebSearchToolResultBlockParamContentParam] tool_use_id: Required[str] type: Required[Literal["web_search_tool_result"]] cache_control: Optional[BetaCacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" caller: Caller """Tool invocation directly from the model.""" beta_web_search_tool_result_block_param_content_param.py000066400000000000000000000011001523216435200360600ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Iterable from typing_extensions import TypeAlias from .beta_web_search_result_block_param import BetaWebSearchResultBlockParam from .beta_web_search_tool_request_error_param import BetaWebSearchToolRequestErrorParam __all__ = ["BetaWebSearchToolResultBlockParamContentParam"] BetaWebSearchToolResultBlockParamContentParam: TypeAlias = Union[ Iterable[BetaWebSearchResultBlockParam], BetaWebSearchToolRequestErrorParam ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_web_search_tool_result_error.py000066400000000000000000000006651523216435200321230ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel from .beta_web_search_tool_result_error_code import BetaWebSearchToolResultErrorCode __all__ = ["BetaWebSearchToolResultError"] class BetaWebSearchToolResultError(BaseModel): error_code: BetaWebSearchToolResultErrorCode type: Literal["web_search_tool_result_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_web_search_tool_result_error_code.py000066400000000000000000000005531523216435200331110ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal, TypeAlias __all__ = ["BetaWebSearchToolResultErrorCode"] BetaWebSearchToolResultErrorCode: TypeAlias = Literal[ "invalid_tool_input", "unavailable", "max_uses_exceeded", "too_many_requests", "query_too_long", "request_too_large" ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_webhook_agent_archived_event_data.py000066400000000000000000000006341523216435200330240ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookAgentArchivedEventData"] class BetaWebhookAgentArchivedEventData(BaseModel): id: str """ID of the agent that triggered the event.""" organization_id: str type: Literal["agent.archived"] workspace_id: str anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_webhook_agent_created_event_data.py000066400000000000000000000006311523216435200326430ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookAgentCreatedEventData"] class BetaWebhookAgentCreatedEventData(BaseModel): id: str """ID of the agent that triggered the event.""" organization_id: str type: Literal["agent.created"] workspace_id: str anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_webhook_agent_deleted_event_data.py000066400000000000000000000006311523216435200326420ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookAgentDeletedEventData"] class BetaWebhookAgentDeletedEventData(BaseModel): id: str """ID of the agent that triggered the event.""" organization_id: str type: Literal["agent.deleted"] workspace_id: str anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_webhook_agent_updated_event_data.py000066400000000000000000000006311523216435200326620ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookAgentUpdatedEventData"] class BetaWebhookAgentUpdatedEventData(BaseModel): id: str """ID of the agent that triggered the event.""" organization_id: str type: Literal["agent.updated"] workspace_id: str anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_webhook_deployment_archived_event_data.py000066400000000000000000000006601523216435200341050ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookDeploymentArchivedEventData"] class BetaWebhookDeploymentArchivedEventData(BaseModel): id: str """ID of the deployment that triggered the event.""" organization_id: str type: Literal["deployment.archived"] workspace_id: str anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_webhook_deployment_created_event_data.py000066400000000000000000000006551523216435200337330ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookDeploymentCreatedEventData"] class BetaWebhookDeploymentCreatedEventData(BaseModel): id: str """ID of the deployment that triggered the event.""" organization_id: str type: Literal["deployment.created"] workspace_id: str anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_webhook_deployment_deleted_event_data.py000066400000000000000000000006551523216435200337320ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookDeploymentDeletedEventData"] class BetaWebhookDeploymentDeletedEventData(BaseModel): id: str """ID of the deployment that triggered the event.""" organization_id: str type: Literal["deployment.deleted"] workspace_id: str anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_webhook_deployment_paused_event_data.py000066400000000000000000000006521523216435200336020ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookDeploymentPausedEventData"] class BetaWebhookDeploymentPausedEventData(BaseModel): id: str """ID of the deployment that triggered the event.""" organization_id: str type: Literal["deployment.paused"] workspace_id: str beta_webhook_deployment_run_failed_event_data.py000066400000000000000000000006701523216435200343520ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookDeploymentRunFailedEventData"] class BetaWebhookDeploymentRunFailedEventData(BaseModel): id: str """ID of the deployment run that triggered the event.""" organization_id: str type: Literal["deployment_run.failed"] workspace_id: str beta_webhook_deployment_run_started_event_data.py000066400000000000000000000006731523216435200345770ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookDeploymentRunStartedEventData"] class BetaWebhookDeploymentRunStartedEventData(BaseModel): id: str """ID of the deployment run that triggered the event.""" organization_id: str type: Literal["deployment_run.started"] workspace_id: str beta_webhook_deployment_run_succeeded_event_data.py000066400000000000000000000007011523216435200350450ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookDeploymentRunSucceededEventData"] class BetaWebhookDeploymentRunSucceededEventData(BaseModel): id: str """ID of the deployment run that triggered the event.""" organization_id: str type: Literal["deployment_run.succeeded"] workspace_id: str anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_webhook_deployment_unpaused_event_data.py000066400000000000000000000006601523216435200341440ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookDeploymentUnpausedEventData"] class BetaWebhookDeploymentUnpausedEventData(BaseModel): id: str """ID of the deployment that triggered the event.""" organization_id: str type: Literal["deployment.unpaused"] workspace_id: str anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_webhook_deployment_updated_event_data.py000066400000000000000000000006551523216435200337520ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookDeploymentUpdatedEventData"] class BetaWebhookDeploymentUpdatedEventData(BaseModel): id: str """ID of the deployment that triggered the event.""" organization_id: str type: Literal["deployment.updated"] workspace_id: str beta_webhook_environment_archived_event_data.py000066400000000000000000000006641523216435200342160ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookEnvironmentArchivedEventData"] class BetaWebhookEnvironmentArchivedEventData(BaseModel): id: str """ID of the environment that triggered the event.""" organization_id: str type: Literal["environment.archived"] workspace_id: str anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_webhook_environment_created_event_data.py000066400000000000000000000006611523216435200341140ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookEnvironmentCreatedEventData"] class BetaWebhookEnvironmentCreatedEventData(BaseModel): id: str """ID of the environment that triggered the event.""" organization_id: str type: Literal["environment.created"] workspace_id: str anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_webhook_environment_deleted_event_data.py000066400000000000000000000006611523216435200341130ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookEnvironmentDeletedEventData"] class BetaWebhookEnvironmentDeletedEventData(BaseModel): id: str """ID of the environment that triggered the event.""" organization_id: str type: Literal["environment.deleted"] workspace_id: str anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_webhook_environment_updated_event_data.py000066400000000000000000000006611523216435200341330ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookEnvironmentUpdatedEventData"] class BetaWebhookEnvironmentUpdatedEventData(BaseModel): id: str """ID of the environment that triggered the event.""" organization_id: str type: Literal["environment.updated"] workspace_id: str anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_webhook_event_data.py000066400000000000000000000146141523216435200300040ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from ..._utils import PropertyInfo from .beta_webhook_agent_created_event_data import BetaWebhookAgentCreatedEventData from .beta_webhook_agent_deleted_event_data import BetaWebhookAgentDeletedEventData from .beta_webhook_agent_updated_event_data import BetaWebhookAgentUpdatedEventData from .beta_webhook_session_idled_event_data import BetaWebhookSessionIdledEventData from .beta_webhook_vault_created_event_data import BetaWebhookVaultCreatedEventData from .beta_webhook_vault_deleted_event_data import BetaWebhookVaultDeletedEventData from .beta_webhook_agent_archived_event_data import BetaWebhookAgentArchivedEventData from .beta_webhook_vault_archived_event_data import BetaWebhookVaultArchivedEventData from .beta_webhook_session_created_event_data import BetaWebhookSessionCreatedEventData from .beta_webhook_session_deleted_event_data import BetaWebhookSessionDeletedEventData from .beta_webhook_session_pending_event_data import BetaWebhookSessionPendingEventData from .beta_webhook_session_running_event_data import BetaWebhookSessionRunningEventData from .beta_webhook_session_updated_event_data import BetaWebhookSessionUpdatedEventData from .beta_webhook_session_archived_event_data import BetaWebhookSessionArchivedEventData from .beta_webhook_deployment_paused_event_data import BetaWebhookDeploymentPausedEventData from .beta_webhook_deployment_created_event_data import BetaWebhookDeploymentCreatedEventData from .beta_webhook_deployment_deleted_event_data import BetaWebhookDeploymentDeletedEventData from .beta_webhook_deployment_updated_event_data import BetaWebhookDeploymentUpdatedEventData from .beta_webhook_deployment_archived_event_data import BetaWebhookDeploymentArchivedEventData from .beta_webhook_deployment_unpaused_event_data import BetaWebhookDeploymentUnpausedEventData from .beta_webhook_environment_created_event_data import BetaWebhookEnvironmentCreatedEventData from .beta_webhook_environment_deleted_event_data import BetaWebhookEnvironmentDeletedEventData from .beta_webhook_environment_updated_event_data import BetaWebhookEnvironmentUpdatedEventData from .beta_webhook_environment_archived_event_data import BetaWebhookEnvironmentArchivedEventData from .beta_webhook_memory_store_created_event_data import BetaWebhookMemoryStoreCreatedEventData from .beta_webhook_memory_store_deleted_event_data import BetaWebhookMemoryStoreDeletedEventData from .beta_webhook_session_status_idled_event_data import BetaWebhookSessionStatusIdledEventData from .beta_webhook_session_thread_idled_event_data import BetaWebhookSessionThreadIdledEventData from .beta_webhook_deployment_run_failed_event_data import BetaWebhookDeploymentRunFailedEventData from .beta_webhook_memory_store_archived_event_data import BetaWebhookMemoryStoreArchivedEventData from .beta_webhook_deployment_run_started_event_data import BetaWebhookDeploymentRunStartedEventData from .beta_webhook_session_thread_created_event_data import BetaWebhookSessionThreadCreatedEventData from .beta_webhook_session_requires_action_event_data import BetaWebhookSessionRequiresActionEventData from .beta_webhook_deployment_run_succeeded_event_data import BetaWebhookDeploymentRunSucceededEventData from .beta_webhook_vault_credential_created_event_data import BetaWebhookVaultCredentialCreatedEventData from .beta_webhook_vault_credential_deleted_event_data import BetaWebhookVaultCredentialDeletedEventData from .beta_webhook_session_status_terminated_event_data import BetaWebhookSessionStatusTerminatedEventData from .beta_webhook_session_thread_terminated_event_data import BetaWebhookSessionThreadTerminatedEventData from .beta_webhook_vault_credential_archived_event_data import BetaWebhookVaultCredentialArchivedEventData from .beta_webhook_session_status_rescheduled_event_data import BetaWebhookSessionStatusRescheduledEventData from .beta_webhook_session_status_run_started_event_data import BetaWebhookSessionStatusRunStartedEventData from .beta_webhook_vault_credential_refresh_failed_event_data import BetaWebhookVaultCredentialRefreshFailedEventData from .beta_webhook_session_outcome_evaluation_ended_event_data import BetaWebhookSessionOutcomeEvaluationEndedEventData __all__ = ["BetaWebhookEventData"] BetaWebhookEventData: TypeAlias = Annotated[ Union[ BetaWebhookSessionCreatedEventData, BetaWebhookSessionPendingEventData, BetaWebhookSessionRunningEventData, BetaWebhookSessionIdledEventData, BetaWebhookSessionRequiresActionEventData, BetaWebhookSessionArchivedEventData, BetaWebhookSessionDeletedEventData, BetaWebhookSessionStatusRescheduledEventData, BetaWebhookSessionStatusRunStartedEventData, BetaWebhookSessionStatusIdledEventData, BetaWebhookSessionStatusTerminatedEventData, BetaWebhookSessionThreadCreatedEventData, BetaWebhookSessionThreadIdledEventData, BetaWebhookSessionThreadTerminatedEventData, BetaWebhookSessionOutcomeEvaluationEndedEventData, BetaWebhookVaultCreatedEventData, BetaWebhookVaultArchivedEventData, BetaWebhookVaultDeletedEventData, BetaWebhookVaultCredentialCreatedEventData, BetaWebhookVaultCredentialArchivedEventData, BetaWebhookVaultCredentialDeletedEventData, BetaWebhookVaultCredentialRefreshFailedEventData, BetaWebhookSessionUpdatedEventData, BetaWebhookAgentCreatedEventData, BetaWebhookAgentArchivedEventData, BetaWebhookAgentDeletedEventData, BetaWebhookDeploymentPausedEventData, BetaWebhookDeploymentRunFailedEventData, BetaWebhookDeploymentCreatedEventData, BetaWebhookDeploymentUpdatedEventData, BetaWebhookDeploymentUnpausedEventData, BetaWebhookAgentUpdatedEventData, BetaWebhookDeploymentArchivedEventData, BetaWebhookDeploymentRunStartedEventData, BetaWebhookDeploymentDeletedEventData, BetaWebhookDeploymentRunSucceededEventData, BetaWebhookEnvironmentCreatedEventData, BetaWebhookEnvironmentUpdatedEventData, BetaWebhookEnvironmentArchivedEventData, BetaWebhookEnvironmentDeletedEventData, BetaWebhookMemoryStoreCreatedEventData, BetaWebhookMemoryStoreArchivedEventData, BetaWebhookMemoryStoreDeletedEventData, ], PropertyInfo(discriminator="type"), ] beta_webhook_memory_store_archived_event_data.py000066400000000000000000000006661523216435200344000ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookMemoryStoreArchivedEventData"] class BetaWebhookMemoryStoreArchivedEventData(BaseModel): id: str """ID of the memory store that triggered the event.""" organization_id: str type: Literal["memory_store.archived"] workspace_id: str beta_webhook_memory_store_created_event_data.py000066400000000000000000000006631523216435200342170ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookMemoryStoreCreatedEventData"] class BetaWebhookMemoryStoreCreatedEventData(BaseModel): id: str """ID of the memory store that triggered the event.""" organization_id: str type: Literal["memory_store.created"] workspace_id: str beta_webhook_memory_store_deleted_event_data.py000066400000000000000000000006631523216435200342160ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookMemoryStoreDeletedEventData"] class BetaWebhookMemoryStoreDeletedEventData(BaseModel): id: str """ID of the memory store that triggered the event.""" organization_id: str type: Literal["memory_store.deleted"] workspace_id: str anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_webhook_session_archived_event_data.py000066400000000000000000000006441523216435200334120ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookSessionArchivedEventData"] class BetaWebhookSessionArchivedEventData(BaseModel): id: str """ID of the session that triggered the event.""" organization_id: str type: Literal["session.archived"] workspace_id: str anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_webhook_session_created_event_data.py000066400000000000000000000006411523216435200332310ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookSessionCreatedEventData"] class BetaWebhookSessionCreatedEventData(BaseModel): id: str """ID of the session that triggered the event.""" organization_id: str type: Literal["session.created"] workspace_id: str anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_webhook_session_deleted_event_data.py000066400000000000000000000006411523216435200332300ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookSessionDeletedEventData"] class BetaWebhookSessionDeletedEventData(BaseModel): id: str """ID of the session that triggered the event.""" organization_id: str type: Literal["session.deleted"] workspace_id: str anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_webhook_session_idled_event_data.py000066400000000000000000000006331523216435200327040ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookSessionIdledEventData"] class BetaWebhookSessionIdledEventData(BaseModel): id: str """ID of the session that triggered the event.""" organization_id: str type: Literal["session.idled"] workspace_id: str beta_webhook_session_outcome_evaluation_ended_event_data.py000066400000000000000000000007201523216435200366020ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookSessionOutcomeEvaluationEndedEventData"] class BetaWebhookSessionOutcomeEvaluationEndedEventData(BaseModel): id: str """ID of the session that triggered the event.""" organization_id: str type: Literal["session.outcome_evaluation_ended"] workspace_id: str anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_webhook_session_pending_event_data.py000066400000000000000000000006411523216435200332460ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookSessionPendingEventData"] class BetaWebhookSessionPendingEventData(BaseModel): id: str """ID of the session that triggered the event.""" organization_id: str type: Literal["session.pending"] workspace_id: str beta_webhook_session_requires_action_event_data.py000066400000000000000000000006671523216435200347470ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookSessionRequiresActionEventData"] class BetaWebhookSessionRequiresActionEventData(BaseModel): id: str """ID of the session that triggered the event.""" organization_id: str type: Literal["session.requires_action"] workspace_id: str anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_webhook_session_running_event_data.py000066400000000000000000000006411523216435200333020ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookSessionRunningEventData"] class BetaWebhookSessionRunningEventData(BaseModel): id: str """ID of the session that triggered the event.""" organization_id: str type: Literal["session.running"] workspace_id: str beta_webhook_session_status_idled_event_data.py000066400000000000000000000006561523216435200342350ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookSessionStatusIdledEventData"] class BetaWebhookSessionStatusIdledEventData(BaseModel): id: str """ID of the session that triggered the event.""" organization_id: str type: Literal["session.status_idled"] workspace_id: str beta_webhook_session_status_rescheduled_event_data.py000066400000000000000000000007001523216435200354310ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookSessionStatusRescheduledEventData"] class BetaWebhookSessionStatusRescheduledEventData(BaseModel): id: str """ID of the session that triggered the event.""" organization_id: str type: Literal["session.status_rescheduled"] workspace_id: str beta_webhook_session_status_run_started_event_data.py000066400000000000000000000006761523216435200355100ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookSessionStatusRunStartedEventData"] class BetaWebhookSessionStatusRunStartedEventData(BaseModel): id: str """ID of the session that triggered the event.""" organization_id: str type: Literal["session.status_run_started"] workspace_id: str beta_webhook_session_status_terminated_event_data.py000066400000000000000000000006751523216435200353110ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookSessionStatusTerminatedEventData"] class BetaWebhookSessionStatusTerminatedEventData(BaseModel): id: str """ID of the session that triggered the event.""" organization_id: str type: Literal["session.status_terminated"] workspace_id: str beta_webhook_session_thread_created_event_data.py000066400000000000000000000010111523216435200344710ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookSessionThreadCreatedEventData"] class BetaWebhookSessionThreadCreatedEventData(BaseModel): id: str """ID of the session that triggered the event.""" organization_id: str session_thread_id: str """ID of the session thread this event refers to.""" type: Literal["session.thread_created"] workspace_id: str beta_webhook_session_thread_idled_event_data.py000066400000000000000000000010031523216435200341440ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookSessionThreadIdledEventData"] class BetaWebhookSessionThreadIdledEventData(BaseModel): id: str """ID of the session that triggered the event.""" organization_id: str session_thread_id: str """ID of the session thread this event refers to.""" type: Literal["session.thread_idled"] workspace_id: str beta_webhook_session_thread_terminated_event_data.py000066400000000000000000000010221523216435200352200ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookSessionThreadTerminatedEventData"] class BetaWebhookSessionThreadTerminatedEventData(BaseModel): id: str """ID of the session that triggered the event.""" organization_id: str session_thread_id: str """ID of the session thread this event refers to.""" type: Literal["session.thread_terminated"] workspace_id: str anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_webhook_session_updated_event_data.py000066400000000000000000000006411523216435200332500ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookSessionUpdatedEventData"] class BetaWebhookSessionUpdatedEventData(BaseModel): id: str """ID of the session that triggered the event.""" organization_id: str type: Literal["session.updated"] workspace_id: str anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_webhook_vault_archived_event_data.py000066400000000000000000000006341523216435200330610ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookVaultArchivedEventData"] class BetaWebhookVaultArchivedEventData(BaseModel): id: str """ID of the vault that triggered the event.""" organization_id: str type: Literal["vault.archived"] workspace_id: str anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_webhook_vault_created_event_data.py000066400000000000000000000006311523216435200327000ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookVaultCreatedEventData"] class BetaWebhookVaultCreatedEventData(BaseModel): id: str """ID of the vault that triggered the event.""" organization_id: str type: Literal["vault.created"] workspace_id: str beta_webhook_vault_credential_archived_event_data.py000066400000000000000000000010161523216435200351670ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookVaultCredentialArchivedEventData"] class BetaWebhookVaultCredentialArchivedEventData(BaseModel): id: str """ID of the vault credential that triggered the event.""" organization_id: str type: Literal["vault_credential.archived"] vault_id: str """ID of the vault that owns this credential.""" workspace_id: str beta_webhook_vault_credential_created_event_data.py000066400000000000000000000010131523216435200350060ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookVaultCredentialCreatedEventData"] class BetaWebhookVaultCredentialCreatedEventData(BaseModel): id: str """ID of the vault credential that triggered the event.""" organization_id: str type: Literal["vault_credential.created"] vault_id: str """ID of the vault that owns this credential.""" workspace_id: str beta_webhook_vault_credential_deleted_event_data.py000066400000000000000000000010131523216435200350050ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookVaultCredentialDeletedEventData"] class BetaWebhookVaultCredentialDeletedEventData(BaseModel): id: str """ID of the vault credential that triggered the event.""" organization_id: str type: Literal["vault_credential.deleted"] vault_id: str """ID of the vault that owns this credential.""" workspace_id: str beta_webhook_vault_credential_refresh_failed_event_data.py000066400000000000000000000010361523216435200363460ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookVaultCredentialRefreshFailedEventData"] class BetaWebhookVaultCredentialRefreshFailedEventData(BaseModel): id: str """ID of the vault credential that triggered the event.""" organization_id: str type: Literal["vault_credential.refresh_failed"] vault_id: str """ID of the vault that owns this credential.""" workspace_id: str anthropic-sdk-python-0.120.2/src/anthropic/types/beta/beta_webhook_vault_deleted_event_data.py000066400000000000000000000006311523216435200326770ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BetaWebhookVaultDeletedEventData"] class BetaWebhookVaultDeletedEventData(BaseModel): id: str """ID of the vault that triggered the event.""" organization_id: str type: Literal["vault.deleted"] workspace_id: str anthropic-sdk-python-0.120.2/src/anthropic/types/beta/deleted_file.py000066400000000000000000000006651523216435200255670ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ..._models import BaseModel __all__ = ["DeletedFile"] class DeletedFile(BaseModel): id: str """ID of the deleted file.""" type: Optional[Literal["file_deleted"]] = None """Deleted object type. For file deletion, this is always `"file_deleted"`. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/deployment_create_params.py000066400000000000000000000054131523216435200302240ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Union, Iterable, Optional from typing_extensions import Required, Annotated, TypeAlias, TypedDict from ..._types import SequenceNotStr from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam from .beta_managed_agents_agent_params import BetaManagedAgentsAgentParams from .beta_managed_agents_schedule_params import BetaManagedAgentsScheduleParams from .beta_managed_agents_file_resource_params import BetaManagedAgentsFileResourceParams from .beta_managed_agents_memory_store_resource_param import BetaManagedAgentsMemoryStoreResourceParam from .beta_managed_agents_deployment_initial_event_params import BetaManagedAgentsDeploymentInitialEventParams from .beta_managed_agents_github_repository_resource_params import BetaManagedAgentsGitHubRepositoryResourceParams __all__ = ["DeploymentCreateParams", "Agent", "Resource"] class DeploymentCreateParams(TypedDict, total=False): agent: Required[Agent] """Agent to deploy. Accepts the `agent` ID string, which pins the latest version, or an `agent` object with both id and version specified. The agent must exist and not be archived. """ environment_id: Required[str] """ ID of the `environment` defining the container configuration for sessions created from this deployment. """ initial_events: Required[Iterable[BetaManagedAgentsDeploymentInitialEventParams]] """Events to send to each session immediately after creation. At least 1, maximum 50. """ name: Required[str] """Human-readable name for the deployment.""" description: Optional[str] """Description of what the deployment does.""" metadata: Dict[str, str] """Arbitrary key-value metadata. Maximum 16 pairs, keys up to 64 chars, values up to 512 chars. """ resources: Iterable[Resource] """Resources (e.g. repositories, files) to mount into each session's container. Maximum 500. """ schedule: Optional[BetaManagedAgentsScheduleParams] """5-field POSIX cron schedule. Literal wall-clock matching in the configured timezone. """ vault_ids: SequenceNotStr[str] """ Vault IDs for stored credentials the agent can use during sessions created from this deployment. Maximum 50. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" Agent: TypeAlias = Union[str, BetaManagedAgentsAgentParams] Resource: TypeAlias = Union[ BetaManagedAgentsGitHubRepositoryResourceParams, BetaManagedAgentsFileResourceParams, BetaManagedAgentsMemoryStoreResourceParam, ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/deployment_list_params.py000066400000000000000000000027711523216435200277400ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Union from datetime import datetime from typing_extensions import Annotated, TypedDict from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam from .beta_managed_agents_deployment_status import BetaManagedAgentsDeploymentStatus __all__ = ["DeploymentListParams"] class DeploymentListParams(TypedDict, total=False): agent_id: str """Filter by agent ID.""" created_at_gte: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[gte]", format="iso8601")] """Return deployments created at or after this time (inclusive).""" created_at_lte: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[lte]", format="iso8601")] """Return deployments created at or before this time (inclusive).""" include_archived: bool """When true, includes archived deployments. Default: false (exclude archived).""" limit: int """Maximum results per page. Default 20, maximum 100.""" page: str """Opaque pagination cursor.""" status: BetaManagedAgentsDeploymentStatus """Filter by status: active or paused. Omit for both. To include archived deployments, use include_archived instead; the two cannot be combined. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/deployment_run_list_params.py000066400000000000000000000040041523216435200306130ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Union from datetime import datetime from typing_extensions import Annotated, TypedDict from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam from .beta_managed_agents_trigger_type import BetaManagedAgentsTriggerType __all__ = ["DeploymentRunListParams"] class DeploymentRunListParams(TypedDict, total=False): created_at_gt: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[gt]", format="iso8601")] """Return runs created strictly after this time (exclusive).""" created_at_gte: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[gte]", format="iso8601")] """Return runs created at or after this time (inclusive).""" created_at_lt: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[lt]", format="iso8601")] """Return runs created strictly before this time (exclusive).""" created_at_lte: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[lte]", format="iso8601")] """Return runs created at or before this time (inclusive).""" deployment_id: str """Filter to a specific deployment. Omit to list across all deployments in the workspace. Filtering by a non-existent deployment_id returns 200 with empty data. """ has_error: bool """ Filter: true for runs with non-null error, false for runs with non-null session_id. Omit for all. """ limit: int """Maximum results per page. Default 20, maximum 1000.""" page: str """Opaque pagination cursor. Pass next_page from the previous response. Invalid or expired cursors return 400. """ trigger_type: BetaManagedAgentsTriggerType """Filter runs by what triggered them. Omit to return all runs.""" betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/deployment_update_params.py000066400000000000000000000056401523216435200302450ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Union, Iterable, Optional from typing_extensions import Annotated, TypeAlias, TypedDict from ..._types import SequenceNotStr from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam from .beta_managed_agents_agent_params import BetaManagedAgentsAgentParams from .beta_managed_agents_schedule_params import BetaManagedAgentsScheduleParams from .beta_managed_agents_file_resource_params import BetaManagedAgentsFileResourceParams from .beta_managed_agents_memory_store_resource_param import BetaManagedAgentsMemoryStoreResourceParam from .beta_managed_agents_deployment_initial_event_params import BetaManagedAgentsDeploymentInitialEventParams from .beta_managed_agents_github_repository_resource_params import BetaManagedAgentsGitHubRepositoryResourceParams __all__ = ["DeploymentUpdateParams", "Agent", "Resource"] class DeploymentUpdateParams(TypedDict, total=False): agent: Agent """Agent to deploy. Accepts the `agent` ID string, which re-pins to the latest version, or an `agent` object with both id and version specified. Omit to preserve. Cannot be cleared. """ description: Optional[str] """Description. Omit to preserve; send empty string or null to clear.""" environment_id: str """ID of the `environment` where sessions run. Omit to preserve. Cannot be cleared. """ initial_events: Iterable[BetaManagedAgentsDeploymentInitialEventParams] """Initial events. Full replacement. Omit to preserve. Cannot be cleared. At least 1, maximum 50. """ metadata: Optional[Dict[str, Optional[str]]] """Metadata patch. Set a key to a string to upsert it, or to null to delete it. Omit the field to preserve. The stored bag is limited to 16 keys (up to 64 chars each) with values up to 512 chars. """ name: str """Human-readable name. Must be non-empty. Omit to preserve. Cannot be cleared.""" resources: Optional[Iterable[Resource]] """Session resources. Full replacement. Omit to preserve; send empty array or null to clear. Maximum 500. """ schedule: Optional[BetaManagedAgentsScheduleParams] """5-field POSIX cron schedule. Literal wall-clock matching in the configured timezone. """ vault_ids: Optional[SequenceNotStr[str]] """Vault IDs. Full replacement. Omit to preserve; send empty array or null to clear. Maximum 50. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" Agent: TypeAlias = Union[str, BetaManagedAgentsAgentParams] Resource: TypeAlias = Union[ BetaManagedAgentsGitHubRepositoryResourceParams, BetaManagedAgentsFileResourceParams, BetaManagedAgentsMemoryStoreResourceParam, ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/dream_create_params.py000066400000000000000000000017041523216435200271330ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Union, Iterable, Optional from typing_extensions import Required, Annotated, TypeAlias, TypedDict from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam from .beta_dream_input_param import BetaDreamInputParam from .beta_dream_model_config_param import BetaDreamModelConfigParam __all__ = ["DreamCreateParams", "Model"] class DreamCreateParams(TypedDict, total=False): inputs: Required[Iterable[BetaDreamInputParam]] model: Required[Model] """Model identifier and configuration applied to every pipeline stage.""" instructions: Optional[str] betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" Model: TypeAlias = Union[str, BetaDreamModelConfigParam] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/dream_list_params.py000066400000000000000000000027141523216435200266450ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Union from datetime import datetime from typing_extensions import Annotated, TypedDict from ..._utils import PropertyInfo from .beta_dream_status import BetaDreamStatus from ..anthropic_beta_param import AnthropicBetaParam __all__ = ["DreamListParams"] class DreamListParams(TypedDict, total=False): created_at_gt: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[gt]", format="iso8601")] """ Return dreams with `created_at` strictly after this timestamp (exclusive lower bound, RFC 3339). Unset applies no lower bound. """ created_at_lt: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[lt]", format="iso8601")] """ Return dreams with `created_at` strictly before this timestamp (exclusive upper bound, RFC 3339). Unset applies no upper bound. """ include_archived: bool """Query parameter for include_archived""" limit: int """Query parameter for limit""" page: str """Query parameter for page""" statuses: List[BetaDreamStatus] """Filter by lifecycle status. Repeat the parameter to match any of multiple statuses. Empty applies no status filter. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/environment_create_params.py000066400000000000000000000026741523216435200304160ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Union, Optional from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam from .beta_cloud_config_params import BetaCloudConfigParams from .beta_self_hosted_config_params import BetaSelfHostedConfigParams __all__ = ["EnvironmentCreateParams", "Config"] class EnvironmentCreateParams(TypedDict, total=False): name: Required[str] """Human-readable name for the environment""" config: Optional[Config] """Environment configuration""" description: Optional[str] """Optional description of the environment""" metadata: Dict[str, str] """User-provided metadata key-value pairs""" scope: Optional[Literal["organization", "account"]] """The visibility scope for this environment. 'organization' makes the environment visible to all accounts. 'account' restricts visibility to the owning account only. Only applicable for self-hosted environments. If not specified, defaults based on organization type. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" Config: TypeAlias = Union[BetaCloudConfigParams, BetaSelfHostedConfigParams] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/environment_list_params.py000066400000000000000000000015251523216435200301200ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Annotated, TypedDict from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam __all__ = ["EnvironmentListParams"] class EnvironmentListParams(TypedDict, total=False): include_archived: bool """Include archived environments in the response""" limit: int """Maximum number of environments to return""" page: Optional[str] """Opaque cursor from previous response for pagination. Pass the `next_page` value from the previous response. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/environment_update_params.py000066400000000000000000000026251523216435200304310ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Union, Optional from typing_extensions import Literal, Annotated, TypeAlias, TypedDict from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam from .beta_cloud_config_params import BetaCloudConfigParams from .beta_self_hosted_config_params import BetaSelfHostedConfigParams __all__ = ["EnvironmentUpdateParams", "Config"] class EnvironmentUpdateParams(TypedDict, total=False): config: Optional[Config] """Updated environment configuration""" description: Optional[str] """Updated description of the environment""" metadata: Dict[str, Optional[str]] """User-provided metadata key-value pairs. Set a value to null or empty string to delete the key. """ name: Optional[str] """Updated name for the environment""" scope: Optional[Literal["organization", "account"]] """The visibility scope for this environment. 'organization' makes the environment visible to all accounts. 'account' restricts visibility to the owning account only. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" Config: TypeAlias = Union[BetaCloudConfigParams, BetaSelfHostedConfigParams] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/environments/000077500000000000000000000000001523216435200253305ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/environments/__init__.py000066400000000000000000000017161523216435200274460ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from .work_list_params import WorkListParams as WorkListParams from .work_poll_params import WorkPollParams as WorkPollParams from .work_stop_params import WorkStopParams as WorkStopParams from .work_update_params import WorkUpdateParams as WorkUpdateParams from .beta_self_hosted_work import BetaSelfHostedWork as BetaSelfHostedWork from .work_heartbeat_params import WorkHeartbeatParams as WorkHeartbeatParams from .beta_session_work_data import BetaSessionWorkData as BetaSessionWorkData from .beta_self_hosted_work_queue_stats import BetaSelfHostedWorkQueueStats as BetaSelfHostedWorkQueueStats from .beta_self_hosted_work_list_response import BetaSelfHostedWorkListResponse as BetaSelfHostedWorkListResponse from .beta_self_hosted_work_heartbeat_response import ( BetaSelfHostedWorkHeartbeatResponse as BetaSelfHostedWorkHeartbeatResponse, ) anthropic-sdk-python-0.120.2/src/anthropic/types/beta/environments/beta_self_hosted_work.py000066400000000000000000000036461523216435200322470ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Dict, Optional from typing_extensions import Literal from ...._models import BaseModel from .beta_session_work_data import BetaSessionWorkData __all__ = ["BetaSelfHostedWork"] class BetaSelfHostedWork(BaseModel): """Work resource representing a unit of work in a self-hosted environment. Work items are queued when sessions are created or when long-dormant sessions receive new messages. The environment worker polls for work to execute in a self-hosted sandbox. """ id: str """Work identifier (e.g., 'work\\__...')""" acknowledged_at: Optional[str] = None """ RFC 3339 timestamp when the work item was acknowledged and assigned to a self-hosted sandbox """ created_at: str """RFC 3339 timestamp when work was created""" data: BetaSessionWorkData """The actual work to be performed""" environment_id: str """Environment identifier this work belongs to (e.g., `env_...`)""" latest_heartbeat_at: Optional[str] = None """RFC 3339 timestamp of the most recent heartbeat""" metadata: Dict[str, str] """User-provided metadata key-value pairs associated with this work item""" secret: Optional[str] = None """Credential payload used by the environment worker to execute this work item. May be populated when polling for work; null on all other retrieval paths. """ started_at: Optional[str] = None """RFC 3339 timestamp when work execution started""" state: Literal["queued", "starting", "active", "stopping", "stopped"] """Current state of the work item""" stop_requested_at: Optional[str] = None """RFC 3339 timestamp when stop was requested""" stopped_at: Optional[str] = None """RFC 3339 timestamp when work execution stopped""" type: Literal["work"] """The type of object (always 'work')""" beta_self_hosted_work_heartbeat_response.py000066400000000000000000000014211523216435200361120ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/environments# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaSelfHostedWorkHeartbeatResponse"] class BetaSelfHostedWorkHeartbeatResponse(BaseModel): """Response after recording a heartbeat for a work item.""" last_heartbeat: str """RFC 3339 timestamp of the actual heartbeat from DB""" lease_extended: bool """Whether the heartbeat succeeded in extending the lease""" state: Literal["queued", "starting", "active", "stopping", "stopped"] """Current state of the work item (active/stopping/stopped)""" ttl_seconds: int """Effective TTL applied to the lease""" type: Literal["work_heartbeat"] """The type of response""" beta_self_hosted_work_list_response.py000066400000000000000000000010401523216435200351230ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/environments# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from ...._models import BaseModel from .beta_self_hosted_work import BetaSelfHostedWork __all__ = ["BetaSelfHostedWorkListResponse"] class BetaSelfHostedWorkListResponse(BaseModel): """Response when listing work items with cursor-based pagination.""" data: List[BetaSelfHostedWork] """List of work items""" next_page: Optional[str] = None """Opaque cursor for fetching the next page of results""" beta_self_hosted_work_queue_stats.py000066400000000000000000000020151523216435200345770ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/environments# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaSelfHostedWorkQueueStats"] class BetaSelfHostedWorkQueueStats(BaseModel): """Statistics about the work queue for an environment. Uses Redis Stream consumer group metrics for O(1) queries. """ depth: int """Number of work items waiting to be picked up (lag from consumer group)""" oldest_queued_at: Optional[str] = None """ RFC 3339 timestamp of oldest item in the work stream (includes both queued and pending items), null if stream empty """ pending: int """Number of work items being processed (polled but not acknowledged)""" type: Literal["work_queue_stats"] """The type of object""" workers_polling: Optional[int] = None """Number of workers that have polled for work in the last 30 seconds. Requires worker_id to be sent with poll requests. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/environments/beta_session_work_data.py000066400000000000000000000010241523216435200324100ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaSessionWorkData"] class BetaSessionWorkData(BaseModel): """Work data for session work items. This resource type is used when work represents a session that needs to be executed in a self-hosted environment. """ id: str """Session identifier (e.g., 'session\\__...')""" type: Literal["session"] """Type of work data""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/environments/work_heartbeat_params.py000066400000000000000000000020131523216435200322420ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Required, Annotated, TypedDict from ...._utils import PropertyInfo from ...anthropic_beta_param import AnthropicBetaParam __all__ = ["WorkHeartbeatParams"] class WorkHeartbeatParams(TypedDict, total=False): environment_id: Required[str] desired_ttl_seconds: Optional[int] """Desired TTL in seconds""" expected_last_heartbeat: Optional[str] """Expected last_heartbeat for conditional update (optimistic concurrency). Use literal 'NO_HEARTBEAT' to claim an unclaimed lease (first heartbeat). For subsequent heartbeats, echo the server's previous last_heartbeat value exactly. Returns 412 Precondition Failed if the actual value doesn't match. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/environments/work_list_params.py000066400000000000000000000012611523216435200312620ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Annotated, TypedDict from ...._utils import PropertyInfo from ...anthropic_beta_param import AnthropicBetaParam __all__ = ["WorkListParams"] class WorkListParams(TypedDict, total=False): limit: int """Maximum number of work items to return""" page: Optional[str] """Opaque cursor from previous response for pagination""" betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/environments/work_poll_params.py000066400000000000000000000021671523216435200312630ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Annotated, TypedDict from ...._utils import PropertyInfo from ...anthropic_beta_param import AnthropicBetaParam __all__ = ["WorkPollParams"] class WorkPollParams(TypedDict, total=False): block_ms: Optional[int] """How long to wait for work to arrive before returning. Must be 1-999 in milliseconds. Defaults to non-blocking (returns immediately if no work is available). """ reclaim_older_than_ms: Optional[int] """Reclaim unacknowledged work items older than this many milliseconds. If omitted, uses the default (5000ms). """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic_worker_id: Annotated[str, PropertyInfo(alias="Anthropic-Worker-ID")] """ Unique identifier for the specific worker polling, used to track aggregated environment-level work metrics in Console """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/environments/work_stop_params.py000066400000000000000000000012201523216435200312670ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List from typing_extensions import Required, Annotated, TypedDict from ...._utils import PropertyInfo from ...anthropic_beta_param import AnthropicBetaParam __all__ = ["WorkStopParams"] class WorkStopParams(TypedDict, total=False): environment_id: Required[str] force: bool """If true, immediately stop work without graceful shutdown""" betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/environments/work_update_params.py000066400000000000000000000014251523216435200315730ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Optional from typing_extensions import Required, Annotated, TypedDict from ...._utils import PropertyInfo from ...anthropic_beta_param import AnthropicBetaParam __all__ = ["WorkUpdateParams"] class WorkUpdateParams(TypedDict, total=False): environment_id: Required[str] metadata: Required[Dict[str, Optional[str]]] """Metadata patch. Set a key to a string to upsert it, or to null to delete it. Omit the field to preserve existing metadata. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/file_list_params.py000066400000000000000000000021261523216435200264710ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List from typing_extensions import Annotated, TypedDict from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam __all__ = ["FileListParams"] class FileListParams(TypedDict, total=False): after_id: str """ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately after this object. """ before_id: str """ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately before this object. """ limit: int """Number of items to return per page. Defaults to `20`. Ranges from `1` to `1000`. """ scope_id: str """Filter by scope ID. Only returns files associated with the specified scope (e.g., a session ID). """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/file_metadata.py000066400000000000000000000020331523216435200257300ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from datetime import datetime from typing_extensions import Literal from ..._models import BaseModel from .beta_file_scope import BetaFileScope __all__ = ["FileMetadata"] class FileMetadata(BaseModel): id: str """Unique object identifier. The format and length of IDs may change over time. """ created_at: datetime """RFC 3339 datetime string representing when the file was created.""" filename: str """Original filename of the uploaded file.""" mime_type: str """MIME type of the file.""" size_bytes: int """Size of the file in bytes.""" type: Literal["file"] """Object type. For files, this is always `"file"`. """ downloadable: Optional[bool] = None """Whether the file can be downloaded.""" scope: Optional[BetaFileScope] = None """ The scope of this file, indicating the context in which it was created (e.g., a session). """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/file_upload_params.py000066400000000000000000000011671523216435200270060ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List from typing_extensions import Required, Annotated, TypedDict from ..._types import FileTypes from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam __all__ = ["FileUploadParams"] class FileUploadParams(TypedDict, total=False): file: Required[FileTypes] """The file to upload""" betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/memory_store_create_params.py000066400000000000000000000024741523216435200305740ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List from typing_extensions import Required, Annotated, TypedDict from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam __all__ = ["MemoryStoreCreateParams"] class MemoryStoreCreateParams(TypedDict, total=False): name: Required[str] """Human-readable name for the store. Required; 1–255 characters; no control characters. The mount-path slug under `/mnt/memory/` is derived from this name (lowercased, non-alphanumeric runs collapsed to a hyphen). Names need not be unique within a workspace. """ description: str """Free-text description of what the store contains, up to 1024 characters. Included in the agent's system prompt when the store is attached, so word it to be useful to the agent. """ metadata: Dict[str, str] """ Arbitrary key-value tags for your own bookkeeping (such as the end user a store belongs to). Up to 16 pairs; keys 1–64 characters; values up to 512 characters. Not visible to the agent. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/memory_store_list_params.py000066400000000000000000000030151523216435200302740ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Union from datetime import datetime from typing_extensions import Annotated, TypedDict from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam __all__ = ["MemoryStoreListParams"] class MemoryStoreListParams(TypedDict, total=False): created_at_gte: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[gte]", format="iso8601")] """Return only stores whose `created_at` is at or after this time (inclusive). Sent on the wire as `created_at[gte]`. """ created_at_lte: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[lte]", format="iso8601")] """Return only stores whose `created_at` is at or before this time (inclusive). Sent on the wire as `created_at[lte]`. """ include_archived: bool """When `true`, archived stores are included in the results. Defaults to `false` (archived stores are excluded). """ limit: int """Maximum number of stores to return per page. Must be between 1 and 100. Defaults to 20 when omitted. """ page: str """Opaque pagination cursor (a `page_...` value). Pass the `next_page` value from a previous response to fetch the next page; omit for the first page. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/memory_store_update_params.py000066400000000000000000000022571523216435200306120ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Optional from typing_extensions import Annotated, TypedDict from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam __all__ = ["MemoryStoreUpdateParams"] class MemoryStoreUpdateParams(TypedDict, total=False): description: Optional[str] """New description for the store, up to 1024 characters. Pass an empty string to clear it. """ metadata: Optional[Dict[str, Optional[str]]] """Metadata patch. Set a key to a string to upsert it, or to null to delete it. Omit the field to preserve. The stored bag is limited to 16 keys (up to 64 chars each) with values up to 512 chars. """ name: Optional[str] """New human-readable name for the store. 1–255 characters; no control characters. Renaming changes the slug used for the store's `mount_path` in sessions created after the update. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/memory_stores/000077500000000000000000000000001523216435200255105ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/memory_stores/__init__.py000066400000000000000000000037451523216435200276320ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from .memory_list_params import MemoryListParams as MemoryListParams from .memory_create_params import MemoryCreateParams as MemoryCreateParams from .memory_delete_params import MemoryDeleteParams as MemoryDeleteParams from .memory_update_params import MemoryUpdateParams as MemoryUpdateParams from .memory_retrieve_params import MemoryRetrieveParams as MemoryRetrieveParams from .beta_managed_agents_actor import BetaManagedAgentsActor as BetaManagedAgentsActor from .beta_managed_agents_memory import BetaManagedAgentsMemory as BetaManagedAgentsMemory from .memory_version_list_params import MemoryVersionListParams as MemoryVersionListParams from .beta_managed_agents_api_actor import BetaManagedAgentsAPIActor as BetaManagedAgentsAPIActor from .beta_managed_agents_user_actor import BetaManagedAgentsUserActor as BetaManagedAgentsUserActor from .memory_version_retrieve_params import MemoryVersionRetrieveParams as MemoryVersionRetrieveParams from .beta_managed_agents_memory_view import BetaManagedAgentsMemoryView as BetaManagedAgentsMemoryView from .beta_managed_agents_memory_prefix import BetaManagedAgentsMemoryPrefix as BetaManagedAgentsMemoryPrefix from .beta_managed_agents_session_actor import BetaManagedAgentsSessionActor as BetaManagedAgentsSessionActor from .beta_managed_agents_deleted_memory import BetaManagedAgentsDeletedMemory as BetaManagedAgentsDeletedMemory from .beta_managed_agents_memory_version import BetaManagedAgentsMemoryVersion as BetaManagedAgentsMemoryVersion from .beta_managed_agents_memory_list_item import BetaManagedAgentsMemoryListItem as BetaManagedAgentsMemoryListItem from .beta_managed_agents_precondition_param import ( BetaManagedAgentsPreconditionParam as BetaManagedAgentsPreconditionParam, ) from .beta_managed_agents_memory_version_operation import ( BetaManagedAgentsMemoryVersionOperation as BetaManagedAgentsMemoryVersionOperation, ) anthropic-sdk-python-0.120.2/src/anthropic/types/beta/memory_stores/beta_managed_agents_actor.py000066400000000000000000000012021523216435200331750ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from ...._utils import PropertyInfo from .beta_managed_agents_api_actor import BetaManagedAgentsAPIActor from .beta_managed_agents_user_actor import BetaManagedAgentsUserActor from .beta_managed_agents_session_actor import BetaManagedAgentsSessionActor __all__ = ["BetaManagedAgentsActor"] BetaManagedAgentsActor: TypeAlias = Annotated[ Union[BetaManagedAgentsSessionActor, BetaManagedAgentsAPIActor, BetaManagedAgentsUserActor], PropertyInfo(discriminator="type"), ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/memory_stores/beta_managed_agents_api_actor.py000066400000000000000000000007741523216435200340430ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsAPIActor"] class BetaManagedAgentsAPIActor(BaseModel): """ Attribution for a write made directly via the public API (outside of any session). """ api_key_id: str """ID of the API key that performed the write. This identifies the key, not the secret. """ type: Literal["api_actor"] beta_managed_agents_deleted_memory.py000066400000000000000000000011761523216435200350160ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/memory_stores# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsDeletedMemory"] class BetaManagedAgentsDeletedMemory(BaseModel): """ Tombstone returned by [Delete a memory](/en/api/beta/memory_stores/memories/delete). The memory's version history persists and remains listable via [List memory versions](/en/api/beta/memory_stores/memory_versions/list) until the store itself is deleted. """ id: str """ID of the deleted memory (a `mem_...` value).""" type: Literal["memory_deleted"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/memory_stores/beta_managed_agents_memory.py000066400000000000000000000046161523216435200334110ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from datetime import datetime from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsMemory"] class BetaManagedAgentsMemory(BaseModel): """ A `memory` object: a single text document at a hierarchical path inside a memory store. The `content` field is populated when `view=full` and `null` when `view=basic`; the `content_size_bytes` and `content_sha256` fields are always populated so sync clients can diff without fetching content. Memories are addressed by their `mem_...` ID; the path is the create key and can be changed via update. """ id: str """Unique identifier for this memory (a `mem_...` value). Stable across renames; use this ID, not the path, to read, update, or delete the memory. """ content_sha256: str """Lowercase hex SHA-256 digest of the UTF-8 `content` bytes (64 characters). The server applies no normalization, so clients can compute the same hash locally for staleness checks and as the value for a `content_sha256` precondition on update. Always populated, regardless of `view`. """ content_size_bytes: int """Size of `content` in bytes (the UTF-8 plaintext length). Always populated, regardless of `view`. """ created_at: datetime """A timestamp in RFC 3339 format""" memory_store_id: str """ID of the memory store this memory belongs to (a `memstore_...` value).""" memory_version_id: str """ ID of the `memory_version` representing this memory's current content (a `memver_...` value). This is the authoritative head pointer; `memory_version` objects do not carry an `is_latest` flag, so compare against this field instead. Enumerate the full history via [List memory versions](/en/api/beta/memory_stores/memory_versions/list). """ path: str """Hierarchical path of the memory within the store, e.g. `/projects/foo/notes.md`. Always starts with `/`. Paths are case-sensitive and unique within a store. Maximum 1,024 bytes. """ type: Literal["memory"] updated_at: datetime """A timestamp in RFC 3339 format""" content: Optional[str] = None """The memory's UTF-8 text content. Populated when `view=full`; `null` when `view=basic`. Maximum 100 kB (102,400 bytes). """ beta_managed_agents_memory_list_item.py000066400000000000000000000010451523216435200353740ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/memory_stores# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from ...._utils import PropertyInfo from .beta_managed_agents_memory import BetaManagedAgentsMemory from .beta_managed_agents_memory_prefix import BetaManagedAgentsMemoryPrefix __all__ = ["BetaManagedAgentsMemoryListItem"] BetaManagedAgentsMemoryListItem: TypeAlias = Annotated[ Union[BetaManagedAgentsMemory, BetaManagedAgentsMemoryPrefix], PropertyInfo(discriminator="type") ] beta_managed_agents_memory_prefix.py000066400000000000000000000016101523216435200346760ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/memory_stores# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsMemoryPrefix"] class BetaManagedAgentsMemoryPrefix(BaseModel): """ A rolled-up directory marker returned by [List memories](/en/api/beta/memory_stores/memories/list) when `depth` is set. Indicates that one or more memories exist deeper than the requested depth under this prefix. This is a list-time rollup, not a stored resource; it has no ID and no lifecycle. Each prefix counts toward the page `limit` and interleaves with `memory` items in path order. """ path: str """The rolled-up path prefix, including a trailing `/` (e.g. `/projects/foo/`). Pass this value as `path_prefix` on a subsequent list call to drill into the directory. """ type: Literal["memory_prefix"] beta_managed_agents_memory_version.py000066400000000000000000000066371523216435200351040ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/memory_stores# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from datetime import datetime from typing_extensions import Literal from ...._models import BaseModel from .beta_managed_agents_actor import BetaManagedAgentsActor from .beta_managed_agents_memory_version_operation import BetaManagedAgentsMemoryVersionOperation __all__ = ["BetaManagedAgentsMemoryVersion"] class BetaManagedAgentsMemoryVersion(BaseModel): """ A `memory_version` object: one immutable, attributed row in a memory's append-only history. Every non-no-op mutation to a memory produces a new version. Versions belong to the store (not the individual memory) and persist after the memory is deleted. Retrieving a redacted version returns 200 with `content`, `path`, `content_size_bytes`, and `content_sha256` set to `null`; branch on `redacted_at`, not HTTP status. """ id: str """Unique identifier for this version (a `memver_...` value).""" created_at: datetime """A timestamp in RFC 3339 format""" memory_id: str """ID of the memory this version snapshots (a `mem_...` value). Remains valid after the memory is deleted; pass it as `memory_id` to [List memory versions](/en/api/beta/memory_stores/memory_versions/list) to retrieve the full lineage including the `deleted` row. """ memory_store_id: str """ID of the memory store this version belongs to (a `memstore_...` value).""" operation: BetaManagedAgentsMemoryVersionOperation """The kind of mutation a `memory_version` records. Every non-no-op mutation to a memory appends exactly one version row with one of these values. """ type: Literal["memory_version"] content: Optional[str] = None """The memory's UTF-8 text content as of this version. `null` when `view=basic`, when `operation` is `deleted`, or when `redacted_at` is set. """ content_sha256: Optional[str] = None """Lowercase hex SHA-256 digest of `content` as of this version (64 characters). `null` when `redacted_at` is set or `operation` is `deleted`. Populated regardless of `view` otherwise. """ content_size_bytes: Optional[int] = None """Size of `content` in bytes as of this version. `null` when `redacted_at` is set or `operation` is `deleted`. Populated regardless of `view` otherwise. """ created_by: Optional[BetaManagedAgentsActor] = None """Identifies who performed a write or redact operation. Captured at write time on the `memory_version` row. The API key that created a session is not recorded on agent writes; attribution answers who made the write, not who is ultimately responsible. Look up session provenance separately via the [Sessions API](/en/api/sessions-retrieve). """ path: Optional[str] = None """The memory's path at the time of this write. `null` if and only if `redacted_at` is set. """ redacted_at: Optional[datetime] = None """A timestamp in RFC 3339 format""" redacted_by: Optional[BetaManagedAgentsActor] = None """Identifies who performed a write or redact operation. Captured at write time on the `memory_version` row. The API key that created a session is not recorded on agent writes; attribution answers who made the write, not who is ultimately responsible. Look up session provenance separately via the [Sessions API](/en/api/sessions-retrieve). """ beta_managed_agents_memory_version_operation.py000066400000000000000000000004371523216435200371540ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/memory_stores# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal, TypeAlias __all__ = ["BetaManagedAgentsMemoryVersionOperation"] BetaManagedAgentsMemoryVersionOperation: TypeAlias = Literal["created", "modified", "deleted"] beta_managed_agents_memory_view.py000066400000000000000000000003661523216435200343620ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/memory_stores# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal, TypeAlias __all__ = ["BetaManagedAgentsMemoryView"] BetaManagedAgentsMemoryView: TypeAlias = Literal["basic", "full"] beta_managed_agents_precondition_param.py000066400000000000000000000021421523216435200356670ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/memory_stores# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsPreconditionParam"] class BetaManagedAgentsPreconditionParam(TypedDict, total=False): """ Optimistic-concurrency precondition: the update applies only if the memory's stored `content_sha256` equals the supplied value. On mismatch, the request returns `memory_precondition_failed_error` (HTTP 409); re-read the memory and retry against the fresh state. If the precondition fails but the stored state already exactly matches the requested `content` and `path`, the server returns 200 instead of 409. """ type: Required[Literal["content_sha256"]] content_sha256: str """ Expected `content_sha256` of the stored memory (64 lowercase hexadecimal characters). Typically the `content_sha256` returned by a prior read or list call. Because the server applies no content normalization, clients can also compute this locally as the SHA-256 of the UTF-8 content bytes. """ beta_managed_agents_session_actor.py000066400000000000000000000011621523216435200346660ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/memory_stores# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsSessionActor"] class BetaManagedAgentsSessionActor(BaseModel): """ Attribution for a write made by an agent during a session, through the mounted filesystem at `/mnt/memory/`. """ session_id: str """ID of the session that performed the write (a `sesn_...` value). Look up the session via [Retrieve a session](/en/api/sessions-retrieve) for further provenance. """ type: Literal["session_actor"] beta_managed_agents_user_actor.py000066400000000000000000000007111523216435200341600ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/memory_stores# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsUserActor"] class BetaManagedAgentsUserActor(BaseModel): """Attribution for a write made by a human user through the Anthropic Console.""" type: Literal["user_actor"] user_id: str """ID of the user who performed the write (a `user_...` value).""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/memory_stores/memory_create_params.py000066400000000000000000000023371523216435200322650ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Required, Annotated, TypedDict from ...._utils import PropertyInfo from ...anthropic_beta_param import AnthropicBetaParam from .beta_managed_agents_memory_view import BetaManagedAgentsMemoryView __all__ = ["MemoryCreateParams"] class MemoryCreateParams(TypedDict, total=False): content: Required[Optional[str]] """UTF-8 text content for the new memory. Maximum 100 kB (102,400 bytes). Required; pass `""` explicitly to create an empty memory. """ path: Required[str] """Hierarchical path for the new memory, e.g. `/projects/foo/notes.md`. Must start with `/`, contain at least one non-empty segment, and be at most 1,024 bytes. Must not contain empty segments, `.` or `..` segments, control or format characters, and must be NFC-normalized. Paths are case-sensitive. """ view: BetaManagedAgentsMemoryView """Query parameter for view""" betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/memory_stores/memory_delete_params.py000066400000000000000000000012351523216435200322600ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List from typing_extensions import Required, Annotated, TypedDict from ...._utils import PropertyInfo from ...anthropic_beta_param import AnthropicBetaParam __all__ = ["MemoryDeleteParams"] class MemoryDeleteParams(TypedDict, total=False): memory_store_id: Required[str] expected_content_sha256: str """Query parameter for expected_content_sha256""" betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/memory_stores/memory_list_params.py000066400000000000000000000033701523216435200317730ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List from typing_extensions import Annotated, TypedDict from ...._utils import PropertyInfo from ...anthropic_beta_param import AnthropicBetaParam from .beta_managed_agents_memory_view import BetaManagedAgentsMemoryView __all__ = ["MemoryListParams"] class MemoryListParams(TypedDict, total=False): depth: int """`0` (or omitted) returns all descendants below `path_prefix` (recursive). `1` returns immediate children only; deeper entries roll up as `memory_prefix` items. `depth=1` behaves like `ls`; omitting `depth` behaves like `find`. """ limit: int """Maximum number of items to return per page. Must be between 1 and 100. Defaults to 20 when omitted. Capped at 20 when `view=full`. Both `memory` and `memory_prefix` items count toward the limit. """ page: str """Opaque pagination cursor (a `page_...` value). Pass the `next_page` value from a previous response to fetch the next page; omit for the first page. """ path_prefix: str """Optional path prefix filter. Must end with `/` (segment-aligned), e.g., `/notes/`. This value appears in request URLs. Do not include secrets or personally identifiable information. """ view: BetaManagedAgentsMemoryView """Which projection of each `memory` to return. Defaults to `basic` (content omitted). `full` populates `content` on each item and caps `limit` at 20; use this as the bulk-read path for export and sync. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/memory_stores/memory_retrieve_params.py000066400000000000000000000013341523216435200326430ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List from typing_extensions import Required, Annotated, TypedDict from ...._utils import PropertyInfo from ...anthropic_beta_param import AnthropicBetaParam from .beta_managed_agents_memory_view import BetaManagedAgentsMemoryView __all__ = ["MemoryRetrieveParams"] class MemoryRetrieveParams(TypedDict, total=False): memory_store_id: Required[str] view: BetaManagedAgentsMemoryView """Query parameter for view""" betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/memory_stores/memory_update_params.py000066400000000000000000000036021523216435200323000ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Required, Annotated, TypedDict from ...._utils import PropertyInfo from ...anthropic_beta_param import AnthropicBetaParam from .beta_managed_agents_memory_view import BetaManagedAgentsMemoryView from .beta_managed_agents_precondition_param import BetaManagedAgentsPreconditionParam __all__ = ["MemoryUpdateParams"] class MemoryUpdateParams(TypedDict, total=False): memory_store_id: Required[str] view: BetaManagedAgentsMemoryView """Query parameter for view""" content: Optional[str] """New UTF-8 text content for the memory. Maximum 100 kB (102,400 bytes). Omit to leave the content unchanged (e.g., for a rename-only update). """ path: Optional[str] """New path for the memory (a rename). Must start with `/`, contain at least one non-empty segment, and be at most 1,024 bytes. Must not contain empty segments, `.` or `..` segments, control or format characters, and must be NFC-normalized. Paths are case-sensitive. The memory's `id` is preserved across renames. Omit to leave the path unchanged. """ precondition: BetaManagedAgentsPreconditionParam """ Optimistic-concurrency precondition: the update applies only if the memory's stored `content_sha256` equals the supplied value. On mismatch, the request returns `memory_precondition_failed_error` (HTTP 409); re-read the memory and retry against the fresh state. If the precondition fails but the stored state already exactly matches the requested `content` and `path`, the server returns 200 instead of 409. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/memory_stores/memory_version_list_params.py000066400000000000000000000030401523216435200335320ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Union from datetime import datetime from typing_extensions import Annotated, TypedDict from ...._utils import PropertyInfo from ...anthropic_beta_param import AnthropicBetaParam from .beta_managed_agents_memory_view import BetaManagedAgentsMemoryView from .beta_managed_agents_memory_version_operation import BetaManagedAgentsMemoryVersionOperation __all__ = ["MemoryVersionListParams"] class MemoryVersionListParams(TypedDict, total=False): api_key_id: str """Query parameter for api_key_id""" created_at_gte: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[gte]", format="iso8601")] """Return versions created at or after this time (inclusive).""" created_at_lte: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[lte]", format="iso8601")] """Return versions created at or before this time (inclusive).""" limit: int """Query parameter for limit""" memory_id: str """Query parameter for memory_id""" operation: BetaManagedAgentsMemoryVersionOperation """Query parameter for operation""" page: str """Query parameter for page""" session_id: str """Query parameter for session_id""" view: BetaManagedAgentsMemoryView """Query parameter for view""" betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" memory_version_retrieve_params.py000066400000000000000000000013521523216435200343310ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/memory_stores# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List from typing_extensions import Required, Annotated, TypedDict from ...._utils import PropertyInfo from ...anthropic_beta_param import AnthropicBetaParam from .beta_managed_agents_memory_view import BetaManagedAgentsMemoryView __all__ = ["MemoryVersionRetrieveParams"] class MemoryVersionRetrieveParams(TypedDict, total=False): memory_store_id: Required[str] view: BetaManagedAgentsMemoryView """Query parameter for view""" betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/message_count_tokens_params.py000066400000000000000000000276501523216435200307470ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Union, Iterable, Optional from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict from ..._utils import PropertyInfo from ..model_param import ModelParam from .beta_tool_param import BetaToolParam from .beta_message_param import BetaMessageParam from ..anthropic_beta_param import AnthropicBetaParam from .beta_text_block_param import BetaTextBlockParam from .beta_mcp_toolset_param import BetaMCPToolsetParam from .beta_tool_choice_param import BetaToolChoiceParam from .beta_output_config_param import BetaOutputConfigParam from .beta_thinking_config_param import BetaThinkingConfigParam from .beta_json_output_format_param import BetaJSONOutputFormatParam from .beta_tool_bash_20241022_param import BetaToolBash20241022Param from .beta_tool_bash_20250124_param import BetaToolBash20250124Param from .beta_memory_tool_20250818_param import BetaMemoryTool20250818Param from .beta_advisor_tool_20260301_param import BetaAdvisorTool20260301Param from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam from .beta_web_fetch_tool_20250910_param import BetaWebFetchTool20250910Param from .beta_web_fetch_tool_20260209_param import BetaWebFetchTool20260209Param from .beta_web_fetch_tool_20260309_param import BetaWebFetchTool20260309Param from .beta_web_fetch_tool_20260318_param import BetaWebFetchTool20260318Param from .beta_web_search_tool_20250305_param import BetaWebSearchTool20250305Param from .beta_web_search_tool_20260209_param import BetaWebSearchTool20260209Param from .beta_web_search_tool_20260318_param import BetaWebSearchTool20260318Param from .beta_context_management_config_param import BetaContextManagementConfigParam from .beta_tool_text_editor_20241022_param import BetaToolTextEditor20241022Param from .beta_tool_text_editor_20250124_param import BetaToolTextEditor20250124Param from .beta_tool_text_editor_20250429_param import BetaToolTextEditor20250429Param from .beta_tool_text_editor_20250728_param import BetaToolTextEditor20250728Param from .beta_tool_computer_use_20241022_param import BetaToolComputerUse20241022Param from .beta_tool_computer_use_20250124_param import BetaToolComputerUse20250124Param from .beta_tool_computer_use_20251124_param import BetaToolComputerUse20251124Param from .beta_code_execution_tool_20250522_param import BetaCodeExecutionTool20250522Param from .beta_code_execution_tool_20250825_param import BetaCodeExecutionTool20250825Param from .beta_code_execution_tool_20260120_param import BetaCodeExecutionTool20260120Param from .beta_code_execution_tool_20260521_param import BetaCodeExecutionTool20260521Param from .beta_tool_search_tool_bm25_20251119_param import BetaToolSearchToolBm25_20251119Param from .beta_tool_search_tool_regex_20251119_param import BetaToolSearchToolRegex20251119Param from .beta_request_mcp_server_url_definition_param import BetaRequestMCPServerURLDefinitionParam __all__ = ["MessageCountTokensParams", "Tool"] class MessageCountTokensParams(TypedDict, total=False): messages: Required[Iterable[BetaMessageParam]] """Input messages. Our models are trained to operate on alternating `user` and `assistant` conversational turns. When creating a new `Message`, you specify the prior conversational turns with the `messages` parameter, and the model then generates the next `Message` in the conversation. Consecutive `user` or `assistant` turns in your request will be combined into a single turn. Each input message must be an object with a `role` and `content`. You can specify a single `user`-role message, or you can include multiple `user` and `assistant` messages. If the final message uses the `assistant` role, the response content will continue immediately from the content in that message. This can be used to constrain part of the model's response. Example with a single `user` message: ```json [{ "role": "user", "content": "Hello, Claude" }] ``` Example with multiple conversational turns: ```json [ { "role": "user", "content": "Hello there." }, { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, { "role": "user", "content": "Can you explain LLMs in plain English?" } ] ``` Example with a partially-filled response from Claude: ```json [ { "role": "user", "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" }, { "role": "assistant", "content": "The best answer is (" } ] ``` Each input message `content` may be either a single `string` or an array of content blocks, where each block has a specific `type`. Using a `string` for `content` is shorthand for an array of one content block of type `"text"`. The following input messages are equivalent: ```json { "role": "user", "content": "Hello, Claude" } ``` ```json { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } ``` See [input examples](https://platform.claude.com/docs/en/build-with-claude/working-with-messages). Note that if you want to include a [system prompt](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role), you can use the top-level `system` parameter — there is no `"system"` role for input messages in the Messages API. There is a limit of 100,000 messages in a single request. """ model: Required[ModelParam] """The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. """ cache_control: Optional[BetaCacheControlEphemeralParam] """ Top-level cache control automatically applies a cache_control marker to the last cacheable block in the request. """ context_management: Optional[BetaContextManagementConfigParam] """Context management configuration. This allows you to control how Claude manages context across multiple requests, such as whether to clear function results or not. """ mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] """MCP servers to be utilized in this request""" output_config: BetaOutputConfigParam """Configuration options for the model's output, such as the output format.""" output_format: Optional[BetaJSONOutputFormatParam] """Deprecated: Use `output_config.format` instead. See [structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs) A schema to specify Claude's output format in responses. This parameter will be removed in a future release. """ speed: Optional[Literal["standard", "fast"]] """Inference speed mode. `fast` provides significantly faster output token generation at premium pricing. Not all models support `fast`; invalid combinations are rejected at create time. """ system: Union[str, Iterable[BetaTextBlockParam]] """System prompt. A system prompt is a way of providing context and instructions to Claude, such as specifying a particular goal or role. See our [guide to system prompts](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role). """ thinking: BetaThinkingConfigParam """Configuration for enabling Claude's extended thinking. When enabled, responses include `thinking` content blocks showing Claude's thinking process before the final answer. Requires a minimum budget of 1,024 tokens and counts towards your `max_tokens` limit. See [extended thinking](https://platform.claude.com/docs/en/build-with-claude/extended-thinking) for details. """ tool_choice: BetaToolChoiceParam """How the model should use the provided tools. The model can use a specific tool, any available tool, decide by itself, or not use tools at all. """ tools: Iterable[Tool] """Definitions of tools that the model may use. If you include `tools` in your API request, the model may return `tool_use` content blocks that represent the model's use of those tools. You can then run those tools using the tool input generated by the model and then optionally return results back to the model using `tool_result` content blocks. There are two types of tools: **client tools** and **server tools**. The behavior described below applies to client tools. For [server tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools), see their individual documentation as each has its own behavior (e.g., the [web search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool)). Each tool definition includes: - `name`: Name of the tool. - `description`: Optional, but strongly-recommended description of the tool. - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the tool `input` shape that the model will produce in `tool_use` output content blocks. For example, if you defined `tools` as: ```json [ { "name": "get_stock_price", "description": "Get the current stock price for a given ticker symbol.", "input_schema": { "type": "object", "properties": { "ticker": { "type": "string", "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." } }, "required": ["ticker"] } } ] ``` And then asked the model "What's the S&P 500 at today?", the model might produce `tool_use` content blocks in the response like this: ```json [ { "type": "tool_use", "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "name": "get_stock_price", "input": { "ticker": "^GSPC" } } ] ``` You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an input, and return the following back to the model in a subsequent `user` message: ```json [ { "type": "tool_result", "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "content": "259.75 USD" } ] ``` Tools can be used for workflows that include running client-side tools and functions, or more generally whenever you want the model to produce a particular JSON structure of output. See our [guide](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview) for more details. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" user_profile_id: Annotated[str, PropertyInfo(alias="anthropic-user-profile-id")] """The user profile ID to attribute this request to. Use when acting on behalf of a party other than your organization. Requires the `user-profiles` beta header. """ Tool: TypeAlias = Union[ BetaToolParam, BetaToolBash20241022Param, BetaToolBash20250124Param, BetaCodeExecutionTool20250522Param, BetaCodeExecutionTool20250825Param, BetaCodeExecutionTool20260120Param, BetaCodeExecutionTool20260521Param, BetaToolComputerUse20241022Param, BetaMemoryTool20250818Param, BetaToolComputerUse20250124Param, BetaToolTextEditor20241022Param, BetaToolComputerUse20251124Param, BetaToolTextEditor20250124Param, BetaToolTextEditor20250429Param, BetaToolTextEditor20250728Param, BetaWebSearchTool20250305Param, BetaWebFetchTool20250910Param, BetaWebSearchTool20260209Param, BetaWebFetchTool20260209Param, BetaWebFetchTool20260309Param, BetaWebSearchTool20260318Param, BetaWebFetchTool20260318Param, BetaAdvisorTool20260301Param, BetaToolSearchToolBm25_20251119Param, BetaToolSearchToolRegex20251119Param, BetaMCPToolsetParam, ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/message_create_params.py000066400000000000000000000366701523216435200275010ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Union, Generic, Iterable, Optional from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict from ..._types import SequenceNotStr from ..._utils import PropertyInfo from ..model_param import ModelParam from .beta_message_param import BetaMessageParam from .beta_metadata_param import BetaMetadataParam from .parsed_beta_message import ResponseFormatT from .beta_fallbacks_param import BetaFallbacksParam from ..anthropic_beta_param import AnthropicBetaParam from .beta_container_params import BetaContainerParams from .beta_text_block_param import BetaTextBlockParam from .beta_tool_union_param import BetaToolUnionParam from .beta_diagnostics_param import BetaDiagnosticsParam from .beta_tool_choice_param import BetaToolChoiceParam from .beta_output_config_param import BetaOutputConfigParam from .beta_thinking_config_param import BetaThinkingConfigParam from .beta_json_output_format_param import BetaJSONOutputFormatParam from .beta_fallback_credit_token_param import BetaFallbackCreditTokenParam from .beta_cache_control_ephemeral_param import BetaCacheControlEphemeralParam from .beta_context_management_config_param import BetaContextManagementConfigParam from .beta_request_mcp_server_url_definition_param import BetaRequestMCPServerURLDefinitionParam __all__ = [ "MessageCreateParamsBase", "Container", "FallbackCreditToken", "MessageCreateParamsNonStreaming", "MessageCreateParamsStreaming", "OutputFormat", ] class MessageCreateParamsBase(TypedDict, total=False): max_tokens: Required[int] """The maximum number of tokens to generate before stopping. Note that our models may stop _before_ reaching this maximum. This parameter only specifies the absolute maximum number of tokens to generate. Set to `0` to populate the [prompt cache](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pre-warming-the-cache) without generating a response. Different models have different maximum values for this parameter. See [models](https://platform.claude.com/docs/en/about-claude/models/overview) for details. """ messages: Required[Iterable[BetaMessageParam]] """Input messages. Our models are trained to operate on alternating `user` and `assistant` conversational turns. When creating a new `Message`, you specify the prior conversational turns with the `messages` parameter, and the model then generates the next `Message` in the conversation. Consecutive `user` or `assistant` turns in your request will be combined into a single turn. Each input message must be an object with a `role` and `content`. You can specify a single `user`-role message, or you can include multiple `user` and `assistant` messages. If the final message uses the `assistant` role, the response content will continue immediately from the content in that message. This can be used to constrain part of the model's response. Example with a single `user` message: ```json [{ "role": "user", "content": "Hello, Claude" }] ``` Example with multiple conversational turns: ```json [ { "role": "user", "content": "Hello there." }, { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, { "role": "user", "content": "Can you explain LLMs in plain English?" } ] ``` Example with a partially-filled response from Claude: ```json [ { "role": "user", "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" }, { "role": "assistant", "content": "The best answer is (" } ] ``` Each input message `content` may be either a single `string` or an array of content blocks, where each block has a specific `type`. Using a `string` for `content` is shorthand for an array of one content block of type `"text"`. The following input messages are equivalent: ```json { "role": "user", "content": "Hello, Claude" } ``` ```json { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } ``` See [input examples](https://platform.claude.com/docs/en/build-with-claude/working-with-messages). Note that if you want to include a [system prompt](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role), you can use the top-level `system` parameter — there is no `"system"` role for input messages in the Messages API. There is a limit of 100,000 messages in a single request. """ model: Required[ModelParam] """The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. """ cache_control: Optional[BetaCacheControlEphemeralParam] """ Top-level cache control automatically applies a cache_control marker to the last cacheable block in the request. """ container: Optional[Container] """Container identifier for reuse across requests.""" context_management: Optional[BetaContextManagementConfigParam] """Context management configuration. This allows you to control how Claude manages context across multiple requests, such as whether to clear function results or not. """ diagnostics: Optional[BetaDiagnosticsParam] """Request-level diagnostics. Currently carries the previous response id for prompt-cache divergence reporting. """ fallback_credit_token: Optional[FallbackCreditToken] """The `fallback_credit_token` from a prior refusal's `stop_details`. When a preceding request was refused and returned a `fallback_credit_token`, pass that code here on the retry to have the retry's cache-creation tokens for the prefix that was warm on the refused model billed at the cache-read rate. Must be redeemed by the same organization and workspace, with the same request body (optionally extended by one appended `assistant` message whose content is the partial text — with any trailing whitespace stripped from the final text block — and paired server-tool blocks streamed before the refusal; the appended-assistant form is not available for requests with `output_format` set or forced `tool_choice`), on an eligible fallback model, on the same platform, and within 5 minutes of the refusal; a mismatch is a 400. A token minted mid-server-tool-loop whose partial content was continuable may only be redeemed with the appended-assistant form — if an exact-body retry is rejected with a 400 saying the token must be redeemed by continuing the partial response, retry with the appended-assistant form instead. When the appended-assistant form is used on a model that otherwise disallows assistant-turn prefill, this token also authorizes that one prefill. """ fallbacks: Optional[BetaFallbacksParam] """ Opt-in server-side retry on one or more substitute models when the requested model declines for policy reasons. Tried in order: if the first entry also declines, the second is tried, and so on. The string "default" requests the requested model's server-defined default fallback configuration. """ inference_geo: Optional[str] """Specifies the geographic region for inference processing. If not specified, the workspace's `default_inference_geo` is used. """ mcp_servers: Iterable[BetaRequestMCPServerURLDefinitionParam] """MCP servers to be utilized in this request""" metadata: BetaMetadataParam """An object describing metadata about the request.""" output_config: BetaOutputConfigParam """Configuration options for the model's output, such as the output format.""" output_format: Optional[BetaJSONOutputFormatParam] """Deprecated: Use `output_config.format` instead. See [structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs) A schema to specify Claude's output format in responses. This parameter will be removed in a future release. """ service_tier: Literal["auto", "standard_only"] """ Determines whether to use priority capacity (if available) or standard capacity for this request. Anthropic offers different levels of service for your API requests. See [service-tiers](https://platform.claude.com/docs/en/api/service-tiers) for details. """ speed: Optional[Literal["standard", "fast"]] """Inference speed mode. `fast` provides significantly faster output token generation at premium pricing. Not all models support `fast`; invalid combinations are rejected at create time. """ stop_sequences: SequenceNotStr[str] """Custom text sequences that will cause the model to stop generating. Our models will normally stop when they have naturally completed their turn, which will result in a response `stop_reason` of `"end_turn"`. If you want the model to stop generating when it encounters custom strings of text, you can use the `stop_sequences` parameter. If the model encounters one of the custom sequences, the response `stop_reason` value will be `"stop_sequence"` and the response `stop_sequence` value will contain the matched stop sequence. """ system: Union[str, Iterable[BetaTextBlockParam]] """System prompt. A system prompt is a way of providing context and instructions to Claude, such as specifying a particular goal or role. See our [guide to system prompts](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role). """ temperature: float """Amount of randomness injected into the response. Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` for analytical / multiple choice, and closer to `1.0` for creative and generative tasks. Note that even with `temperature` of `0.0`, the results will not be fully deterministic. """ thinking: BetaThinkingConfigParam """Configuration for enabling Claude's extended thinking. When enabled, responses include `thinking` content blocks showing Claude's thinking process before the final answer. Requires a minimum budget of 1,024 tokens and counts towards your `max_tokens` limit. See [extended thinking](https://platform.claude.com/docs/en/build-with-claude/extended-thinking) for details. """ tool_choice: BetaToolChoiceParam """How the model should use the provided tools. The model can use a specific tool, any available tool, decide by itself, or not use tools at all. """ tools: Iterable[BetaToolUnionParam] """Definitions of tools that the model may use. If you include `tools` in your API request, the model may return `tool_use` content blocks that represent the model's use of those tools. You can then run those tools using the tool input generated by the model and then optionally return results back to the model using `tool_result` content blocks. There are two types of tools: **client tools** and **server tools**. The behavior described below applies to client tools. For [server tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools), see their individual documentation as each has its own behavior (e.g., the [web search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool)). Each tool definition includes: - `name`: Name of the tool. - `description`: Optional, but strongly-recommended description of the tool. - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the tool `input` shape that the model will produce in `tool_use` output content blocks. For example, if you defined `tools` as: ```json [ { "name": "get_stock_price", "description": "Get the current stock price for a given ticker symbol.", "input_schema": { "type": "object", "properties": { "ticker": { "type": "string", "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." } }, "required": ["ticker"] } } ] ``` And then asked the model "What's the S&P 500 at today?", the model might produce `tool_use` content blocks in the response like this: ```json [ { "type": "tool_use", "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "name": "get_stock_price", "input": { "ticker": "^GSPC" } } ] ``` You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an input, and return the following back to the model in a subsequent `user` message: ```json [ { "type": "tool_result", "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "content": "259.75 USD" } ] ``` Tools can be used for workflows that include running client-side tools and functions, or more generally whenever you want the model to produce a particular JSON structure of output. See our [guide](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview) for more details. """ top_k: int """Only sample from the top K options for each subsequent token. Used to remove "long tail" low probability responses. [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). Recommended for advanced use cases only. """ top_p: float """Use nucleus sampling. In nucleus sampling, we compute the cumulative distribution over all the options for each subsequent token in decreasing probability order and cut it off once it reaches a particular probability specified by `top_p`. Recommended for advanced use cases only. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" user_profile_id: Annotated[str, PropertyInfo(alias="anthropic-user-profile-id")] """The user profile ID to attribute this request to. Use when acting on behalf of a party other than your organization. Requires the `user-profiles` beta header. """ Container: TypeAlias = Union[BetaContainerParams, str] FallbackCreditToken: TypeAlias = Union[str, BetaFallbackCreditTokenParam] class ParseMessageCreateParamsBase(MessageCreateParamsBase, Generic[ResponseFormatT]): output_format: type[ResponseFormatT] # type: ignore[misc] class OutputFormat(TypedDict, total=False): schema: Required[object] """The JSON schema of the format""" type: Required[Literal["json_schema"]] class MessageCreateParamsNonStreaming(MessageCreateParamsBase, total=False): stream: Literal[False] """Whether to incrementally stream the response using server-sent events. See [streaming](https://platform.claude.com/docs/en/build-with-claude/streaming) for details. """ class MessageCreateParamsStreaming(MessageCreateParamsBase): stream: Required[Literal[True]] """Whether to incrementally stream the response using server-sent events. See [streaming](https://platform.claude.com/docs/en/build-with-claude/streaming) for details. """ MessageCreateParams = Union[MessageCreateParamsNonStreaming, MessageCreateParamsStreaming] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/messages/000077500000000000000000000000001523216435200244105ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/messages/__init__.py000066400000000000000000000022621523216435200265230ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from .batch_list_params import BatchListParams as BatchListParams from .beta_message_batch import BetaMessageBatch as BetaMessageBatch from .batch_create_params import BatchCreateParams as BatchCreateParams from .beta_message_batch_result import BetaMessageBatchResult as BetaMessageBatchResult from .beta_deleted_message_batch import BetaDeletedMessageBatch as BetaDeletedMessageBatch from .beta_message_batch_errored_result import BetaMessageBatchErroredResult as BetaMessageBatchErroredResult from .beta_message_batch_expired_result import BetaMessageBatchExpiredResult as BetaMessageBatchExpiredResult from .beta_message_batch_request_counts import BetaMessageBatchRequestCounts as BetaMessageBatchRequestCounts from .beta_message_batch_canceled_result import BetaMessageBatchCanceledResult as BetaMessageBatchCanceledResult from .beta_message_batch_succeeded_result import BetaMessageBatchSucceededResult as BetaMessageBatchSucceededResult from .beta_message_batch_individual_response import ( BetaMessageBatchIndividualResponse as BetaMessageBatchIndividualResponse, ) anthropic-sdk-python-0.120.2/src/anthropic/types/beta/messages/batch_create_params.py000066400000000000000000000034061523216435200307340ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Iterable from typing_extensions import Required, Annotated, TypedDict from ...._utils import PropertyInfo from ...anthropic_beta_param import AnthropicBetaParam from ..message_create_params import MessageCreateParamsNonStreaming __all__ = ["BatchCreateParams", "Request"] class BatchCreateParams(TypedDict, total=False): requests: Required[Iterable[Request]] """List of requests for prompt completion. Each is an individual request to create a Message. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" user_profile_id: Annotated[str, PropertyInfo(alias="anthropic-user-profile-id")] """The user profile ID to attribute the requests in this batch to. Use when acting on behalf of a party other than your organization. Requires the `user-profiles` beta header. Applies to every request in the batch; an individual request whose `user_profile_id` body field conflicts with this header is errored. """ class Request(TypedDict, total=False): custom_id: Required[str] """Developer-provided ID created for each request in a Message Batch. Useful for matching results to requests, as results may be given out of request order. Must be unique for each request within the Message Batch. """ params: Required[MessageCreateParamsNonStreaming] """Messages API creation parameters for the individual request. See the [Messages API reference](https://platform.claude.com/docs/en/api/messages) for full documentation on available parameters. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/messages/batch_list_params.py000066400000000000000000000017221523216435200304430ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List from typing_extensions import Annotated, TypedDict from ...._utils import PropertyInfo from ...anthropic_beta_param import AnthropicBetaParam __all__ = ["BatchListParams"] class BatchListParams(TypedDict, total=False): after_id: str """ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately after this object. """ before_id: str """ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately before this object. """ limit: int """Number of items to return per page. Defaults to `20`. Ranges from `1` to `1000`. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/messages/beta_deleted_message_batch.py000066400000000000000000000006661523216435200322400ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaDeletedMessageBatch"] class BetaDeletedMessageBatch(BaseModel): id: str """ID of the Message Batch.""" type: Literal["message_batch_deleted"] """Deleted object type. For Message Batches, this is always `"message_batch_deleted"`. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/messages/beta_message_batch.py000066400000000000000000000046051523216435200305470ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from datetime import datetime from typing_extensions import Literal from ...._models import BaseModel from .beta_message_batch_request_counts import BetaMessageBatchRequestCounts __all__ = ["BetaMessageBatch"] class BetaMessageBatch(BaseModel): id: str """Unique object identifier. The format and length of IDs may change over time. """ archived_at: Optional[datetime] = None """ RFC 3339 datetime string representing the time at which the Message Batch was archived and its results became unavailable. """ cancel_initiated_at: Optional[datetime] = None """ RFC 3339 datetime string representing the time at which cancellation was initiated for the Message Batch. Specified only if cancellation was initiated. """ created_at: datetime """ RFC 3339 datetime string representing the time at which the Message Batch was created. """ ended_at: Optional[datetime] = None """ RFC 3339 datetime string representing the time at which processing for the Message Batch ended. Specified only once processing ends. Processing ends when every request in a Message Batch has either succeeded, errored, canceled, or expired. """ expires_at: datetime """ RFC 3339 datetime string representing the time at which the Message Batch will expire and end processing, which is 24 hours after creation. """ processing_status: Literal["in_progress", "canceling", "ended"] """Processing status of the Message Batch.""" request_counts: BetaMessageBatchRequestCounts """Tallies requests within the Message Batch, categorized by their status. Requests start as `processing` and move to one of the other statuses only once processing of the entire batch ends. The sum of all values always matches the total number of requests in the batch. """ results_url: Optional[str] = None """URL to a `.jsonl` file containing the results of the Message Batch requests. Specified only once processing ends. Results in the file are not guaranteed to be in the same order as requests. Use the `custom_id` field to match results to requests. """ type: Literal["message_batch"] """Object type. For Message Batches, this is always `"message_batch"`. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/messages/beta_message_batch_canceled_result.py000066400000000000000000000004371523216435200337620ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaMessageBatchCanceledResult"] class BetaMessageBatchCanceledResult(BaseModel): type: Literal["canceled"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/messages/beta_message_batch_errored_result.py000066400000000000000000000005571523216435200336710ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel from ...beta_error_response import BetaErrorResponse __all__ = ["BetaMessageBatchErroredResult"] class BetaMessageBatchErroredResult(BaseModel): error: BetaErrorResponse type: Literal["errored"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/messages/beta_message_batch_expired_result.py000066400000000000000000000004341523216435200336610ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaMessageBatchExpiredResult"] class BetaMessageBatchExpiredResult(BaseModel): type: Literal["expired"] beta_message_batch_individual_response.py000066400000000000000000000016651523216435200346210ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/messages# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from ...._models import BaseModel from .beta_message_batch_result import BetaMessageBatchResult __all__ = ["BetaMessageBatchIndividualResponse"] class BetaMessageBatchIndividualResponse(BaseModel): """ This is a single line in the response `.jsonl` file and does not represent the response as a whole. """ custom_id: str """Developer-provided ID created for each request in a Message Batch. Useful for matching results to requests, as results may be given out of request order. Must be unique for each request within the Message Batch. """ result: BetaMessageBatchResult """Processing result for this request. Contains a Message output if processing was successful, an error response if processing failed, or the reason why processing was not attempted, such as cancellation or expiration. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/messages/beta_message_batch_request_counts.py000066400000000000000000000017531523216435200337130ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from ...._models import BaseModel __all__ = ["BetaMessageBatchRequestCounts"] class BetaMessageBatchRequestCounts(BaseModel): canceled: int """Number of requests in the Message Batch that have been canceled. This is zero until processing of the entire Message Batch has ended. """ errored: int """Number of requests in the Message Batch that encountered an error. This is zero until processing of the entire Message Batch has ended. """ expired: int """Number of requests in the Message Batch that have expired. This is zero until processing of the entire Message Batch has ended. """ processing: int """Number of requests in the Message Batch that are processing.""" succeeded: int """Number of requests in the Message Batch that have completed successfully. This is zero until processing of the entire Message Batch has ended. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/messages/beta_message_batch_result.py000066400000000000000000000014631523216435200321440ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from ...._utils import PropertyInfo from .beta_message_batch_errored_result import BetaMessageBatchErroredResult from .beta_message_batch_expired_result import BetaMessageBatchExpiredResult from .beta_message_batch_canceled_result import BetaMessageBatchCanceledResult from .beta_message_batch_succeeded_result import BetaMessageBatchSucceededResult __all__ = ["BetaMessageBatchResult"] BetaMessageBatchResult: TypeAlias = Annotated[ Union[ BetaMessageBatchSucceededResult, BetaMessageBatchErroredResult, BetaMessageBatchCanceledResult, BetaMessageBatchExpiredResult, ], PropertyInfo(discriminator="type"), ] beta_message_batch_succeeded_result.py000066400000000000000000000005431523216435200340670ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/messages# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel from ..beta_message import BetaMessage __all__ = ["BetaMessageBatchSucceededResult"] class BetaMessageBatchSucceededResult(BaseModel): message: BetaMessage type: Literal["succeeded"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/model_list_params.py000066400000000000000000000017201523216435200266510ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List from typing_extensions import Annotated, TypedDict from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam __all__ = ["ModelListParams"] class ModelListParams(TypedDict, total=False): after_id: str """ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately after this object. """ before_id: str """ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately before this object. """ limit: int """Number of items to return per page. Defaults to `20`. Ranges from `1` to `1000`. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/parsed_beta_message.py000066400000000000000000000056211523216435200271340ustar00rootroot00000000000000from __future__ import annotations from typing import TYPE_CHECKING, List, Union, Generic, Optional from typing_extensions import TypeVar, Annotated, TypeAlias from ..._utils import PropertyInfo from .beta_message import BetaMessage from .beta_text_block import BetaTextBlock from .beta_fallback_block import BetaFallbackBlock from .beta_thinking_block import BetaThinkingBlock from .beta_tool_use_block import BetaToolUseBlock from .beta_compaction_block import BetaCompactionBlock from .beta_mcp_tool_use_block import BetaMCPToolUseBlock from .beta_mcp_tool_result_block import BetaMCPToolResultBlock from .beta_server_tool_use_block import BetaServerToolUseBlock from .beta_container_upload_block import BetaContainerUploadBlock from .beta_redacted_thinking_block import BetaRedactedThinkingBlock from .beta_advisor_tool_result_block import BetaAdvisorToolResultBlock from .beta_web_fetch_tool_result_block import BetaWebFetchToolResultBlock from .beta_web_search_tool_result_block import BetaWebSearchToolResultBlock from .beta_tool_search_tool_result_block import BetaToolSearchToolResultBlock from .beta_code_execution_tool_result_block import BetaCodeExecutionToolResultBlock from .beta_bash_code_execution_tool_result_block import BetaBashCodeExecutionToolResultBlock from .beta_text_editor_code_execution_tool_result_block import BetaTextEditorCodeExecutionToolResultBlock ResponseFormatT = TypeVar("ResponseFormatT", default=None) __all__ = [ "ParsedBetaTextBlock", "ParsedBetaContentBlock", "ParsedBetaMessage", ] class ParsedBetaTextBlock(BetaTextBlock, Generic[ResponseFormatT]): parsed_output: Optional[ResponseFormatT] = None __api_exclude__ = {"parsed_output"} # Note that generic unions are not valid for pydantic at runtime ParsedBetaContentBlock: TypeAlias = Annotated[ Union[ ParsedBetaTextBlock[ResponseFormatT], BetaThinkingBlock, BetaRedactedThinkingBlock, BetaToolUseBlock, BetaServerToolUseBlock, BetaWebSearchToolResultBlock, BetaWebFetchToolResultBlock, BetaAdvisorToolResultBlock, BetaCodeExecutionToolResultBlock, BetaBashCodeExecutionToolResultBlock, BetaTextEditorCodeExecutionToolResultBlock, BetaToolSearchToolResultBlock, BetaMCPToolUseBlock, BetaMCPToolResultBlock, BetaContainerUploadBlock, BetaCompactionBlock, BetaFallbackBlock, ], PropertyInfo(discriminator="type"), ] class ParsedBetaMessage(BetaMessage, Generic[ResponseFormatT]): if TYPE_CHECKING: content: List[ParsedBetaContentBlock[ResponseFormatT]] # type: ignore[assignment] else: content: List[ParsedBetaContentBlock] @property def parsed_output(self) -> Optional[ResponseFormatT]: for content in self.content: if content.type == "text" and content.parsed_output: return content.parsed_output return None anthropic-sdk-python-0.120.2/src/anthropic/types/beta/session_create_params.py000066400000000000000000000053471523216435200275350ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Union, Iterable, Optional from typing_extensions import Required, Annotated, TypeAlias, TypedDict from ..._types import SequenceNotStr from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam from .beta_managed_agents_agent_params import BetaManagedAgentsAgentParams from .beta_managed_agents_file_resource_params import BetaManagedAgentsFileResourceParams from .beta_managed_agents_agent_with_overrides_params import BetaManagedAgentsAgentWithOverridesParams from .beta_managed_agents_memory_store_resource_param import BetaManagedAgentsMemoryStoreResourceParam from .beta_managed_agents_github_repository_resource_params import BetaManagedAgentsGitHubRepositoryResourceParams from .sessions.beta_managed_agents_user_message_event_params import BetaManagedAgentsUserMessageEventParams from .sessions.beta_managed_agents_user_define_outcome_event_params import BetaManagedAgentsUserDefineOutcomeEventParams __all__ = ["SessionCreateParams", "Agent", "InitialEvent", "Resource"] class SessionCreateParams(TypedDict, total=False): agent: Required[Agent] """Agent identifier. Accepts the `agent` ID string, which pins the latest version for the session, or an `agent` object with both id and version specified. """ environment_id: Required[str] """ID of the `environment` defining the container configuration for this session.""" initial_events: Iterable[InitialEvent] """Initial events to send to the `session` at creation, processed in order. Supports `user.message` and `user.define_outcome` events. Maximum 50 events. """ metadata: Dict[str, str] """Arbitrary key-value metadata attached to the session. Maximum 16 pairs, keys up to 64 chars, values up to 512 chars. """ resources: Iterable[Resource] """Resources (e.g. repositories, files) to mount into the session's container.""" title: Optional[str] """Human-readable session title.""" vault_ids: SequenceNotStr[str] """Vault IDs for stored credentials the agent can use during the session.""" betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" Agent: TypeAlias = Union[str, BetaManagedAgentsAgentParams, BetaManagedAgentsAgentWithOverridesParams] InitialEvent: TypeAlias = Union[BetaManagedAgentsUserMessageEventParams, BetaManagedAgentsUserDefineOutcomeEventParams] Resource: TypeAlias = Union[ BetaManagedAgentsGitHubRepositoryResourceParams, BetaManagedAgentsFileResourceParams, BetaManagedAgentsMemoryStoreResourceParam, ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/session_list_params.py000066400000000000000000000042721523216435200272410ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Union from datetime import datetime from typing_extensions import Literal, Annotated, TypedDict from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam __all__ = ["SessionListParams"] class SessionListParams(TypedDict, total=False): agent_id: str """Filter sessions created with this agent ID.""" agent_version: int """Filter by agent version. Only applies when agent_id is also set.""" created_at_gt: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[gt]", format="iso8601")] """Return sessions created after this time (exclusive).""" created_at_gte: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[gte]", format="iso8601")] """Return sessions created at or after this time (inclusive).""" created_at_lt: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[lt]", format="iso8601")] """Return sessions created before this time (exclusive).""" created_at_lte: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[lte]", format="iso8601")] """Return sessions created at or before this time (inclusive).""" deployment_id: str """Filter sessions created by this deployment ID.""" include_archived: bool """When true, includes archived sessions. Default: false (exclude archived).""" limit: int """Maximum number of results to return.""" memory_store_id: str """ Filter sessions whose resources contain a memory_store with this memory store ID. """ order: Literal["asc", "desc"] """Sort direction for results, ordered by created_at. Defaults to desc (newest first). """ page: str """Opaque pagination cursor from a previous response.""" statuses: List[Literal["rescheduling", "running", "idle", "terminated"]] """Filter by session status. Repeat the parameter to match any of multiple statuses. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/session_update_params.py000066400000000000000000000026341523216435200275500ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Optional from typing_extensions import Annotated, TypedDict from ..._types import SequenceNotStr from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam from .beta_managed_agents_session_agent_update_param import BetaManagedAgentsSessionAgentUpdateParam __all__ = ["SessionUpdateParams"] class SessionUpdateParams(TypedDict, total=False): agent: BetaManagedAgentsSessionAgentUpdateParam """Mid-session agent configuration update. Only `tools` and `mcp_servers` are updatable. Full replacement: the provided array becomes the new value. To preserve existing entries, GET the session, modify the array, and POST it back. """ metadata: Optional[Dict[str, Optional[str]]] """Metadata patch. Set a key to a string to upsert it, or to null to delete it. Omit the field to preserve. """ title: Optional[str] """Human-readable session title.""" vault_ids: SequenceNotStr[str] """Vault IDs (`vlt_*`) to attach to the session. Not yet supported; requests setting this field are rejected. Reserved for future use. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions/000077500000000000000000000000001523216435200244475ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions/__init__.py000066400000000000000000000351741523216435200265720ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from .event_list_params import EventListParams as EventListParams from .event_send_params import EventSendParams as EventSendParams from .thread_list_params import ThreadListParams as ThreadListParams from .event_stream_params import EventStreamParams as EventStreamParams from .resource_add_params import ResourceAddParams as ResourceAddParams from .resource_list_params import ResourceListParams as ResourceListParams from .resource_update_params import ResourceUpdateParams as ResourceUpdateParams from .resource_update_response import ResourceUpdateResponse as ResourceUpdateResponse from .resource_retrieve_response import ResourceRetrieveResponse as ResourceRetrieveResponse from .beta_managed_agents_text_block import BetaManagedAgentsTextBlock as BetaManagedAgentsTextBlock from .beta_managed_agents_file_rubric import BetaManagedAgentsFileRubric as BetaManagedAgentsFileRubric from .beta_managed_agents_image_block import BetaManagedAgentsImageBlock as BetaManagedAgentsImageBlock from .beta_managed_agents_text_rubric import BetaManagedAgentsTextRubric as BetaManagedAgentsTextRubric from .beta_managed_agents_event_params import BetaManagedAgentsEventParams as BetaManagedAgentsEventParams from .beta_managed_agents_billing_error import BetaManagedAgentsBillingError as BetaManagedAgentsBillingError from .beta_managed_agents_file_resource import BetaManagedAgentsFileResource as BetaManagedAgentsFileResource from .beta_managed_agents_session_event import BetaManagedAgentsSessionEvent as BetaManagedAgentsSessionEvent from .beta_managed_agents_unknown_error import BetaManagedAgentsUnknownError as BetaManagedAgentsUnknownError from .beta_managed_agents_document_block import BetaManagedAgentsDocumentBlock as BetaManagedAgentsDocumentBlock from .beta_managed_agents_session_thread import BetaManagedAgentsSessionThread as BetaManagedAgentsSessionThread from .beta_managed_agents_session_end_turn import BetaManagedAgentsSessionEndTurn as BetaManagedAgentsSessionEndTurn from .beta_managed_agents_session_resource import BetaManagedAgentsSessionResource as BetaManagedAgentsSessionResource from .beta_managed_agents_span_model_usage import BetaManagedAgentsSpanModelUsage as BetaManagedAgentsSpanModelUsage from .beta_managed_agents_text_block_param import BetaManagedAgentsTextBlockParam as BetaManagedAgentsTextBlockParam from .beta_managed_agents_url_image_source import BetaManagedAgentsURLImageSource as BetaManagedAgentsURLImageSource from .beta_managed_agents_file_image_source import BetaManagedAgentsFileImageSource as BetaManagedAgentsFileImageSource from .beta_managed_agents_image_block_param import BetaManagedAgentsImageBlockParam as BetaManagedAgentsImageBlockParam from .beta_managed_agents_file_rubric_params import ( BetaManagedAgentsFileRubricParams as BetaManagedAgentsFileRubricParams, ) from .beta_managed_agents_text_rubric_params import ( BetaManagedAgentsTextRubricParams as BetaManagedAgentsTextRubricParams, ) from .beta_managed_agents_user_message_event import ( BetaManagedAgentsUserMessageEvent as BetaManagedAgentsUserMessageEvent, ) from .beta_managed_agents_agent_message_event import ( BetaManagedAgentsAgentMessageEvent as BetaManagedAgentsAgentMessageEvent, ) from .beta_managed_agents_base64_image_source import ( BetaManagedAgentsBase64ImageSource as BetaManagedAgentsBase64ImageSource, ) from .beta_managed_agents_search_result_block import ( BetaManagedAgentsSearchResultBlock as BetaManagedAgentsSearchResultBlock, ) from .beta_managed_agents_send_session_events import ( BetaManagedAgentsSendSessionEvents as BetaManagedAgentsSendSessionEvents, ) from .beta_managed_agents_session_error_event import ( BetaManagedAgentsSessionErrorEvent as BetaManagedAgentsSessionErrorEvent, ) from .beta_managed_agents_url_document_source import ( BetaManagedAgentsURLDocumentSource as BetaManagedAgentsURLDocumentSource, ) from .beta_managed_agents_agent_thinking_event import ( BetaManagedAgentsAgentThinkingEvent as BetaManagedAgentsAgentThinkingEvent, ) from .beta_managed_agents_agent_tool_use_event import ( BetaManagedAgentsAgentToolUseEvent as BetaManagedAgentsAgentToolUseEvent, ) from .beta_managed_agents_document_block_param import ( BetaManagedAgentsDocumentBlockParam as BetaManagedAgentsDocumentBlockParam, ) from .beta_managed_agents_file_document_source import ( BetaManagedAgentsFileDocumentSource as BetaManagedAgentsFileDocumentSource, ) from .beta_managed_agents_session_thread_stats import ( BetaManagedAgentsSessionThreadStats as BetaManagedAgentsSessionThreadStats, ) from .beta_managed_agents_session_thread_usage import ( BetaManagedAgentsSessionThreadUsage as BetaManagedAgentsSessionThreadUsage, ) from .beta_managed_agents_user_interrupt_event import ( BetaManagedAgentsUserInterruptEvent as BetaManagedAgentsUserInterruptEvent, ) from .beta_managed_agents_memory_store_resource import ( BetaManagedAgentsMemoryStoreResource as BetaManagedAgentsMemoryStoreResource, ) from .beta_managed_agents_retry_status_retrying import ( BetaManagedAgentsRetryStatusRetrying as BetaManagedAgentsRetryStatusRetrying, ) from .beta_managed_agents_retry_status_terminal import ( BetaManagedAgentsRetryStatusTerminal as BetaManagedAgentsRetryStatusTerminal, ) from .beta_managed_agents_search_result_content import ( BetaManagedAgentsSearchResultContent as BetaManagedAgentsSearchResultContent, ) from .beta_managed_agents_session_deleted_event import ( BetaManagedAgentsSessionDeletedEvent as BetaManagedAgentsSessionDeletedEvent, ) from .beta_managed_agents_session_thread_status import ( BetaManagedAgentsSessionThreadStatus as BetaManagedAgentsSessionThreadStatus, ) from .beta_managed_agents_stream_session_events import ( BetaManagedAgentsStreamSessionEvents as BetaManagedAgentsStreamSessionEvents, ) from .beta_managed_agents_base64_document_source import ( BetaManagedAgentsBase64DocumentSource as BetaManagedAgentsBase64DocumentSource, ) from .beta_managed_agents_model_overloaded_error import ( BetaManagedAgentsModelOverloadedError as BetaManagedAgentsModelOverloadedError, ) from .beta_managed_agents_retry_status_exhausted import ( BetaManagedAgentsRetryStatusExhausted as BetaManagedAgentsRetryStatusExhausted, ) from .beta_managed_agents_url_image_source_param import ( BetaManagedAgentsURLImageSourceParam as BetaManagedAgentsURLImageSourceParam, ) from .beta_managed_agents_agent_tool_result_event import ( BetaManagedAgentsAgentToolResultEvent as BetaManagedAgentsAgentToolResultEvent, ) from .beta_managed_agents_delete_session_resource import ( BetaManagedAgentsDeleteSessionResource as BetaManagedAgentsDeleteSessionResource, ) from .beta_managed_agents_file_image_source_param import ( BetaManagedAgentsFileImageSourceParam as BetaManagedAgentsFileImageSourceParam, ) from .beta_managed_agents_search_result_citations import ( BetaManagedAgentsSearchResultCitations as BetaManagedAgentsSearchResultCitations, ) from .beta_managed_agents_session_requires_action import ( BetaManagedAgentsSessionRequiresAction as BetaManagedAgentsSessionRequiresAction, ) from .beta_managed_agents_agent_mcp_tool_use_event import ( BetaManagedAgentsAgentMCPToolUseEvent as BetaManagedAgentsAgentMCPToolUseEvent, ) from .beta_managed_agents_model_rate_limited_error import ( BetaManagedAgentsModelRateLimitedError as BetaManagedAgentsModelRateLimitedError, ) from .beta_managed_agents_base64_image_source_param import ( BetaManagedAgentsBase64ImageSourceParam as BetaManagedAgentsBase64ImageSourceParam, ) from .beta_managed_agents_search_result_block_param import ( BetaManagedAgentsSearchResultBlockParam as BetaManagedAgentsSearchResultBlockParam, ) from .beta_managed_agents_session_retries_exhausted import ( BetaManagedAgentsSessionRetriesExhausted as BetaManagedAgentsSessionRetriesExhausted, ) from .beta_managed_agents_session_status_idle_event import ( BetaManagedAgentsSessionStatusIdleEvent as BetaManagedAgentsSessionStatusIdleEvent, ) from .beta_managed_agents_url_document_source_param import ( BetaManagedAgentsURLDocumentSourceParam as BetaManagedAgentsURLDocumentSourceParam, ) from .beta_managed_agents_user_define_outcome_event import ( BetaManagedAgentsUserDefineOutcomeEvent as BetaManagedAgentsUserDefineOutcomeEvent, ) from .beta_managed_agents_user_message_event_params import ( BetaManagedAgentsUserMessageEventParams as BetaManagedAgentsUserMessageEventParams, ) from .beta_managed_agents_file_document_source_param import ( BetaManagedAgentsFileDocumentSourceParam as BetaManagedAgentsFileDocumentSourceParam, ) from .beta_managed_agents_github_repository_resource import ( BetaManagedAgentsGitHubRepositoryResource as BetaManagedAgentsGitHubRepositoryResource, ) from .beta_managed_agents_model_request_failed_error import ( BetaManagedAgentsModelRequestFailedError as BetaManagedAgentsModelRequestFailedError, ) from .beta_managed_agents_plain_text_document_source import ( BetaManagedAgentsPlainTextDocumentSource as BetaManagedAgentsPlainTextDocumentSource, ) from .beta_managed_agents_agent_custom_tool_use_event import ( BetaManagedAgentsAgentCustomToolUseEvent as BetaManagedAgentsAgentCustomToolUseEvent, ) from .beta_managed_agents_agent_mcp_tool_result_event import ( BetaManagedAgentsAgentMCPToolResultEvent as BetaManagedAgentsAgentMCPToolResultEvent, ) from .beta_managed_agents_mcp_connection_failed_error import ( BetaManagedAgentsMCPConnectionFailedError as BetaManagedAgentsMCPConnectionFailedError, ) from .beta_managed_agents_search_result_content_param import ( BetaManagedAgentsSearchResultContentParam as BetaManagedAgentsSearchResultContentParam, ) from .beta_managed_agents_system_message_event_params import ( BetaManagedAgentsSystemMessageEventParams as BetaManagedAgentsSystemMessageEventParams, ) from .beta_managed_agents_user_interrupt_event_params import ( BetaManagedAgentsUserInterruptEventParams as BetaManagedAgentsUserInterruptEventParams, ) from .beta_managed_agents_base64_document_source_param import ( BetaManagedAgentsBase64DocumentSourceParam as BetaManagedAgentsBase64DocumentSourceParam, ) from .beta_managed_agents_session_status_running_event import ( BetaManagedAgentsSessionStatusRunningEvent as BetaManagedAgentsSessionStatusRunningEvent, ) from .beta_managed_agents_session_thread_created_event import ( BetaManagedAgentsSessionThreadCreatedEvent as BetaManagedAgentsSessionThreadCreatedEvent, ) from .beta_managed_agents_span_model_request_end_event import ( BetaManagedAgentsSpanModelRequestEndEvent as BetaManagedAgentsSpanModelRequestEndEvent, ) from .beta_managed_agents_stream_session_thread_events import ( BetaManagedAgentsStreamSessionThreadEvents as BetaManagedAgentsStreamSessionThreadEvents, ) from .beta_managed_agents_user_tool_confirmation_event import ( BetaManagedAgentsUserToolConfirmationEvent as BetaManagedAgentsUserToolConfirmationEvent, ) from .beta_managed_agents_search_result_citations_param import ( BetaManagedAgentsSearchResultCitationsParam as BetaManagedAgentsSearchResultCitationsParam, ) from .beta_managed_agents_user_custom_tool_result_event import ( BetaManagedAgentsUserCustomToolResultEvent as BetaManagedAgentsUserCustomToolResultEvent, ) from .beta_managed_agents_user_tool_result_event_params import ( BetaManagedAgentsUserToolResultEventParams as BetaManagedAgentsUserToolResultEventParams, ) from .beta_managed_agents_span_model_request_start_event import ( BetaManagedAgentsSpanModelRequestStartEvent as BetaManagedAgentsSpanModelRequestStartEvent, ) from .beta_managed_agents_agent_thread_message_sent_event import ( BetaManagedAgentsAgentThreadMessageSentEvent as BetaManagedAgentsAgentThreadMessageSentEvent, ) from .beta_managed_agents_mcp_authentication_failed_error import ( BetaManagedAgentsMCPAuthenticationFailedError as BetaManagedAgentsMCPAuthenticationFailedError, ) from .beta_managed_agents_session_status_terminated_event import ( BetaManagedAgentsSessionStatusTerminatedEvent as BetaManagedAgentsSessionStatusTerminatedEvent, ) from .beta_managed_agents_plain_text_document_source_param import ( BetaManagedAgentsPlainTextDocumentSourceParam as BetaManagedAgentsPlainTextDocumentSourceParam, ) from .beta_managed_agents_session_status_rescheduled_event import ( BetaManagedAgentsSessionStatusRescheduledEvent as BetaManagedAgentsSessionStatusRescheduledEvent, ) from .beta_managed_agents_session_thread_status_idle_event import ( BetaManagedAgentsSessionThreadStatusIdleEvent as BetaManagedAgentsSessionThreadStatusIdleEvent, ) from .beta_managed_agents_user_define_outcome_event_params import ( BetaManagedAgentsUserDefineOutcomeEventParams as BetaManagedAgentsUserDefineOutcomeEventParams, ) from .beta_managed_agents_credential_host_unreachable_error import ( BetaManagedAgentsCredentialHostUnreachableError as BetaManagedAgentsCredentialHostUnreachableError, ) from .beta_managed_agents_span_outcome_evaluation_end_event import ( BetaManagedAgentsSpanOutcomeEvaluationEndEvent as BetaManagedAgentsSpanOutcomeEvaluationEndEvent, ) from .beta_managed_agents_agent_thread_message_received_event import ( BetaManagedAgentsAgentThreadMessageReceivedEvent as BetaManagedAgentsAgentThreadMessageReceivedEvent, ) from .beta_managed_agents_session_thread_status_running_event import ( BetaManagedAgentsSessionThreadStatusRunningEvent as BetaManagedAgentsSessionThreadStatusRunningEvent, ) from .beta_managed_agents_span_outcome_evaluation_start_event import ( BetaManagedAgentsSpanOutcomeEvaluationStartEvent as BetaManagedAgentsSpanOutcomeEvaluationStartEvent, ) from .beta_managed_agents_user_tool_confirmation_event_params import ( BetaManagedAgentsUserToolConfirmationEventParams as BetaManagedAgentsUserToolConfirmationEventParams, ) from .beta_managed_agents_agent_thread_context_compacted_event import ( BetaManagedAgentsAgentThreadContextCompactedEvent as BetaManagedAgentsAgentThreadContextCompactedEvent, ) from .beta_managed_agents_user_custom_tool_result_event_params import ( BetaManagedAgentsUserCustomToolResultEventParams as BetaManagedAgentsUserCustomToolResultEventParams, ) from .beta_managed_agents_span_outcome_evaluation_ongoing_event import ( BetaManagedAgentsSpanOutcomeEvaluationOngoingEvent as BetaManagedAgentsSpanOutcomeEvaluationOngoingEvent, ) from .beta_managed_agents_session_thread_status_terminated_event import ( BetaManagedAgentsSessionThreadStatusTerminatedEvent as BetaManagedAgentsSessionThreadStatusTerminatedEvent, ) from .beta_managed_agents_session_thread_status_rescheduled_event import ( BetaManagedAgentsSessionThreadStatusRescheduledEvent as BetaManagedAgentsSessionThreadStatusRescheduledEvent, ) beta_managed_agents_agent_custom_tool_use_event.py000066400000000000000000000021421523216435200365530ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Dict, Optional from datetime import datetime from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsAgentCustomToolUseEvent"] class BetaManagedAgentsAgentCustomToolUseEvent(BaseModel): """Event emitted when the agent calls a custom tool. The session goes idle until the client sends a `user.custom_tool_result` event with the result. """ id: str """Unique identifier for this event.""" input: Dict[str, object] """Input parameters for the tool call.""" name: str """Name of the custom tool being called.""" processed_at: datetime """A timestamp in RFC 3339 format""" type: Literal["agent.custom_tool_use"] session_thread_id: Optional[str] = None """ When set, this event was cross-posted from a subagent's thread to surface its custom tool use on the primary thread's stream. Empty on the thread's own events. Echo this on a `user.custom_tool_result` event to route the result back. """ beta_managed_agents_agent_mcp_tool_result_event.py000066400000000000000000000027471523216435200365550ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Union, Optional from datetime import datetime from typing_extensions import Literal, Annotated, TypeAlias from ...._utils import PropertyInfo from ...._models import BaseModel from .beta_managed_agents_text_block import BetaManagedAgentsTextBlock from .beta_managed_agents_image_block import BetaManagedAgentsImageBlock from .beta_managed_agents_document_block import BetaManagedAgentsDocumentBlock from .beta_managed_agents_search_result_block import BetaManagedAgentsSearchResultBlock __all__ = ["BetaManagedAgentsAgentMCPToolResultEvent", "Content"] Content: TypeAlias = Annotated[ Union[ BetaManagedAgentsTextBlock, BetaManagedAgentsImageBlock, BetaManagedAgentsDocumentBlock, BetaManagedAgentsSearchResultBlock, ], PropertyInfo(discriminator="type"), ] class BetaManagedAgentsAgentMCPToolResultEvent(BaseModel): """Event representing the result of an MCP tool execution.""" id: str """Unique identifier for this event.""" mcp_tool_use_id: str """The id of the `agent.mcp_tool_use` event this result corresponds to.""" processed_at: datetime """A timestamp in RFC 3339 format""" type: Literal["agent.mcp_tool_result"] content: Optional[List[Content]] = None """The result content returned by the tool.""" is_error: Optional[bool] = None """Whether the tool execution resulted in an error.""" beta_managed_agents_agent_mcp_tool_use_event.py000066400000000000000000000023121523216435200360170ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Dict, Optional from datetime import datetime from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsAgentMCPToolUseEvent"] class BetaManagedAgentsAgentMCPToolUseEvent(BaseModel): """Event emitted when the agent invokes a tool provided by an MCP server.""" id: str """Unique identifier for this event.""" input: Dict[str, object] """Input parameters for the tool call.""" mcp_server_name: str """Name of the MCP server providing the tool.""" name: str """Name of the MCP tool being used.""" processed_at: datetime """A timestamp in RFC 3339 format""" type: Literal["agent.mcp_tool_use"] evaluated_permission: Optional[Literal["allow", "ask", "deny"]] = None """AgentEvaluatedPermission enum""" session_thread_id: Optional[str] = None """ When set, this event was cross-posted from a subagent's thread to surface its permission request on the primary thread's stream. Empty on the thread's own events. Echo this on a `user.tool_confirmation` event to route the approval back. """ beta_managed_agents_agent_message_event.py000066400000000000000000000013241523216435200347550ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List from datetime import datetime from typing_extensions import Literal from ...._models import BaseModel from .beta_managed_agents_text_block import BetaManagedAgentsTextBlock __all__ = ["BetaManagedAgentsAgentMessageEvent"] class BetaManagedAgentsAgentMessageEvent(BaseModel): """An agent response event in the session conversation.""" id: str """Unique identifier for this event.""" content: List[BetaManagedAgentsTextBlock] """Array of text blocks comprising the agent response.""" processed_at: datetime """A timestamp in RFC 3339 format""" type: Literal["agent.message"] beta_managed_agents_agent_thinking_event.py000066400000000000000000000011201523216435200351360ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from datetime import datetime from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsAgentThinkingEvent"] class BetaManagedAgentsAgentThinkingEvent(BaseModel): """Indicates the agent is making forward progress via extended thinking. A progress signal, not a content carrier. """ id: str """Unique identifier for this event.""" processed_at: datetime """A timestamp in RFC 3339 format""" type: Literal["agent.thinking"] beta_managed_agents_agent_thread_context_compacted_event.py000066400000000000000000000011211523216435200403560ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from datetime import datetime from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsAgentThreadContextCompactedEvent"] class BetaManagedAgentsAgentThreadContextCompactedEvent(BaseModel): """Indicates that context compaction (summarization) occurred during the session.""" id: str """Unique identifier for this event.""" processed_at: datetime """A timestamp in RFC 3339 format""" type: Literal["agent.thread_context_compacted"] beta_managed_agents_agent_thread_message_received_event.py000066400000000000000000000026471523216435200401630ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Union, Optional from datetime import datetime from typing_extensions import Literal, Annotated, TypeAlias from ...._utils import PropertyInfo from ...._models import BaseModel from .beta_managed_agents_text_block import BetaManagedAgentsTextBlock from .beta_managed_agents_image_block import BetaManagedAgentsImageBlock from .beta_managed_agents_document_block import BetaManagedAgentsDocumentBlock __all__ = ["BetaManagedAgentsAgentThreadMessageReceivedEvent", "Content"] Content: TypeAlias = Annotated[ Union[BetaManagedAgentsTextBlock, BetaManagedAgentsImageBlock, BetaManagedAgentsDocumentBlock], PropertyInfo(discriminator="type"), ] class BetaManagedAgentsAgentThreadMessageReceivedEvent(BaseModel): """ Delivery event written to the target thread's input stream when an agent-to-agent message arrives. """ id: str """Unique identifier for this event.""" content: List[Content] """Message content blocks.""" from_session_thread_id: str """Public `sthr_` ID of the thread that sent the message.""" processed_at: datetime """A timestamp in RFC 3339 format""" type: Literal["agent.thread_message_received"] from_agent_name: Optional[str] = None """Name of the callable agent this message came from. Absent when received from the primary agent. """ beta_managed_agents_agent_thread_message_sent_event.py000066400000000000000000000026241523216435200373410ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Union, Optional from datetime import datetime from typing_extensions import Literal, Annotated, TypeAlias from ...._utils import PropertyInfo from ...._models import BaseModel from .beta_managed_agents_text_block import BetaManagedAgentsTextBlock from .beta_managed_agents_image_block import BetaManagedAgentsImageBlock from .beta_managed_agents_document_block import BetaManagedAgentsDocumentBlock __all__ = ["BetaManagedAgentsAgentThreadMessageSentEvent", "Content"] Content: TypeAlias = Annotated[ Union[BetaManagedAgentsTextBlock, BetaManagedAgentsImageBlock, BetaManagedAgentsDocumentBlock], PropertyInfo(discriminator="type"), ] class BetaManagedAgentsAgentThreadMessageSentEvent(BaseModel): """ Observability event emitted to the sender's output stream when an agent-to-agent message is sent. """ id: str """Unique identifier for this event.""" content: List[Content] """Message content blocks.""" processed_at: datetime """A timestamp in RFC 3339 format""" to_session_thread_id: str """Public `sthr_` ID of the thread the message was sent to.""" type: Literal["agent.thread_message_sent"] to_agent_name: Optional[str] = None """Name of the callable agent this message was sent to. Absent when sent to the primary agent. """ beta_managed_agents_agent_tool_result_event.py000066400000000000000000000027271523216435200357140ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Union, Optional from datetime import datetime from typing_extensions import Literal, Annotated, TypeAlias from ...._utils import PropertyInfo from ...._models import BaseModel from .beta_managed_agents_text_block import BetaManagedAgentsTextBlock from .beta_managed_agents_image_block import BetaManagedAgentsImageBlock from .beta_managed_agents_document_block import BetaManagedAgentsDocumentBlock from .beta_managed_agents_search_result_block import BetaManagedAgentsSearchResultBlock __all__ = ["BetaManagedAgentsAgentToolResultEvent", "Content"] Content: TypeAlias = Annotated[ Union[ BetaManagedAgentsTextBlock, BetaManagedAgentsImageBlock, BetaManagedAgentsDocumentBlock, BetaManagedAgentsSearchResultBlock, ], PropertyInfo(discriminator="type"), ] class BetaManagedAgentsAgentToolResultEvent(BaseModel): """Event representing the result of an agent tool execution.""" id: str """Unique identifier for this event.""" processed_at: datetime """A timestamp in RFC 3339 format""" tool_use_id: str """The id of the `agent.tool_use` event this result corresponds to.""" type: Literal["agent.tool_result"] content: Optional[List[Content]] = None """The result content returned by the tool.""" is_error: Optional[bool] = None """Whether the tool execution resulted in an error.""" beta_managed_agents_agent_tool_use_event.py000066400000000000000000000021501523216435200351600ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Dict, Optional from datetime import datetime from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsAgentToolUseEvent"] class BetaManagedAgentsAgentToolUseEvent(BaseModel): """Event emitted when the agent invokes a built-in agent tool.""" id: str """Unique identifier for this event.""" input: Dict[str, object] """Input parameters for the tool call.""" name: str """Name of the agent tool being used.""" processed_at: datetime """A timestamp in RFC 3339 format""" type: Literal["agent.tool_use"] evaluated_permission: Optional[Literal["allow", "ask", "deny"]] = None """AgentEvaluatedPermission enum""" session_thread_id: Optional[str] = None """ When set, this event was cross-posted from a subagent's thread to surface its permission request on the primary thread's stream. Empty on the thread's own events. Echo this on a `user.tool_confirmation` event to route the approval back. """ beta_managed_agents_base64_document_source.py000066400000000000000000000007371523216435200353230ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsBase64DocumentSource"] class BetaManagedAgentsBase64DocumentSource(BaseModel): """Base64-encoded document data.""" data: str """Base64-encoded document data.""" media_type: str """MIME type of the document (e.g., "application/pdf").""" type: Literal["base64"] beta_managed_agents_base64_document_source_param.py000066400000000000000000000010521523216435200364720ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsBase64DocumentSourceParam"] class BetaManagedAgentsBase64DocumentSourceParam(TypedDict, total=False): """Base64-encoded document data.""" data: Required[str] """Base64-encoded document data.""" media_type: Required[str] """MIME type of the document (e.g., "application/pdf").""" type: Required[Literal["base64"]] beta_managed_agents_base64_image_source.py000066400000000000000000000010011523216435200345500ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsBase64ImageSource"] class BetaManagedAgentsBase64ImageSource(BaseModel): """Base64-encoded image data.""" data: str """Base64-encoded image data.""" media_type: str """ MIME type of the image (e.g., "image/png", "image/jpeg", "image/gif", "image/webp"). """ type: Literal["base64"] beta_managed_agents_base64_image_source_param.py000066400000000000000000000011141523216435200357350ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsBase64ImageSourceParam"] class BetaManagedAgentsBase64ImageSourceParam(TypedDict, total=False): """Base64-encoded image data.""" data: Required[str] """Base64-encoded image data.""" media_type: Required[str] """ MIME type of the image (e.g., "image/png", "image/jpeg", "image/gif", "image/webp"). """ type: Required[Literal["base64"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions/beta_managed_agents_billing_error.py000066400000000000000000000024111523216435200336600ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Literal, Annotated, TypeAlias from ...._utils import PropertyInfo from ...._models import BaseModel from .beta_managed_agents_retry_status_retrying import BetaManagedAgentsRetryStatusRetrying from .beta_managed_agents_retry_status_terminal import BetaManagedAgentsRetryStatusTerminal from .beta_managed_agents_retry_status_exhausted import BetaManagedAgentsRetryStatusExhausted __all__ = ["BetaManagedAgentsBillingError", "RetryStatus"] RetryStatus: TypeAlias = Annotated[ Union[ BetaManagedAgentsRetryStatusRetrying, BetaManagedAgentsRetryStatusExhausted, BetaManagedAgentsRetryStatusTerminal, ], PropertyInfo(discriminator="type"), ] class BetaManagedAgentsBillingError(BaseModel): """ The caller's organization or workspace cannot make model requests — out of credits or spend limit reached. Retrying with the same credentials will not succeed; the caller must resolve the billing state. """ message: str """Human-readable error description.""" retry_status: RetryStatus """What the client should do next in response to this error.""" type: Literal["billing_error"] beta_managed_agents_credential_host_unreachable_error.py000066400000000000000000000026171523216435200376710ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Literal, Annotated, TypeAlias from ...._utils import PropertyInfo from ...._models import BaseModel from .beta_managed_agents_retry_status_retrying import BetaManagedAgentsRetryStatusRetrying from .beta_managed_agents_retry_status_terminal import BetaManagedAgentsRetryStatusTerminal from .beta_managed_agents_retry_status_exhausted import BetaManagedAgentsRetryStatusExhausted __all__ = ["BetaManagedAgentsCredentialHostUnreachableError", "RetryStatus"] RetryStatus: TypeAlias = Annotated[ Union[ BetaManagedAgentsRetryStatusRetrying, BetaManagedAgentsRetryStatusExhausted, BetaManagedAgentsRetryStatusTerminal, ], PropertyInfo(discriminator="type"), ] class BetaManagedAgentsCredentialHostUnreachableError(BaseModel): """ An `environment_variable` credential's `auth.networking.allowed_hosts` includes a host the environment's network policy does not permit. """ credential_id: str """ID of the affected credential.""" message: str """Human-readable error description.""" retry_status: RetryStatus """What the client should do next in response to this error.""" type: Literal["credential_host_unreachable_error"] vault_id: str """ID of the vault containing the affected credential.""" beta_managed_agents_delete_session_resource.py000066400000000000000000000005721523216435200356720ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsDeleteSessionResource"] class BetaManagedAgentsDeleteSessionResource(BaseModel): """Confirmation of resource deletion.""" id: str type: Literal["session_resource_deleted"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions/beta_managed_agents_document_block.py000066400000000000000000000025671523216435200340330ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union, Optional from typing_extensions import Literal, Annotated, TypeAlias from ...._utils import PropertyInfo from ...._models import BaseModel from .beta_managed_agents_url_document_source import BetaManagedAgentsURLDocumentSource from .beta_managed_agents_file_document_source import BetaManagedAgentsFileDocumentSource from .beta_managed_agents_base64_document_source import BetaManagedAgentsBase64DocumentSource from .beta_managed_agents_plain_text_document_source import BetaManagedAgentsPlainTextDocumentSource __all__ = ["BetaManagedAgentsDocumentBlock", "Source"] Source: TypeAlias = Annotated[ Union[ BetaManagedAgentsBase64DocumentSource, BetaManagedAgentsPlainTextDocumentSource, BetaManagedAgentsURLDocumentSource, BetaManagedAgentsFileDocumentSource, ], PropertyInfo(discriminator="type"), ] class BetaManagedAgentsDocumentBlock(BaseModel): """ Document content, either specified directly as base64 data, as text, or as a reference via a URL. """ source: Source """Union type for document source variants.""" type: Literal["document"] context: Optional[str] = None """Additional context about the document for the model.""" title: Optional[str] = None """The title of the document.""" beta_managed_agents_document_block_param.py000066400000000000000000000025561523216435200351320ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .beta_managed_agents_url_document_source_param import BetaManagedAgentsURLDocumentSourceParam from .beta_managed_agents_file_document_source_param import BetaManagedAgentsFileDocumentSourceParam from .beta_managed_agents_base64_document_source_param import BetaManagedAgentsBase64DocumentSourceParam from .beta_managed_agents_plain_text_document_source_param import BetaManagedAgentsPlainTextDocumentSourceParam __all__ = ["BetaManagedAgentsDocumentBlockParam", "Source"] Source: TypeAlias = Union[ BetaManagedAgentsBase64DocumentSourceParam, BetaManagedAgentsPlainTextDocumentSourceParam, BetaManagedAgentsURLDocumentSourceParam, BetaManagedAgentsFileDocumentSourceParam, ] class BetaManagedAgentsDocumentBlockParam(TypedDict, total=False): """ Document content, either specified directly as base64 data, as text, or as a reference via a URL. """ source: Required[Source] """Union type for document source variants.""" type: Required[Literal["document"]] context: Optional[str] """Additional context about the document for the model.""" title: Optional[str] """The title of the document.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions/beta_managed_agents_event_params.py000066400000000000000000000025571523216435200335260ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from typing_extensions import TypeAlias from .beta_managed_agents_user_message_event_params import BetaManagedAgentsUserMessageEventParams from .beta_managed_agents_system_message_event_params import BetaManagedAgentsSystemMessageEventParams from .beta_managed_agents_user_interrupt_event_params import BetaManagedAgentsUserInterruptEventParams from .beta_managed_agents_user_tool_result_event_params import BetaManagedAgentsUserToolResultEventParams from .beta_managed_agents_user_define_outcome_event_params import BetaManagedAgentsUserDefineOutcomeEventParams from .beta_managed_agents_user_tool_confirmation_event_params import BetaManagedAgentsUserToolConfirmationEventParams from .beta_managed_agents_user_custom_tool_result_event_params import BetaManagedAgentsUserCustomToolResultEventParams __all__ = ["BetaManagedAgentsEventParams"] BetaManagedAgentsEventParams: TypeAlias = Union[ BetaManagedAgentsUserMessageEventParams, BetaManagedAgentsUserInterruptEventParams, BetaManagedAgentsUserToolConfirmationEventParams, BetaManagedAgentsUserCustomToolResultEventParams, BetaManagedAgentsUserDefineOutcomeEventParams, BetaManagedAgentsUserToolResultEventParams, BetaManagedAgentsSystemMessageEventParams, ] beta_managed_agents_file_document_source.py000066400000000000000000000006161523216435200351520ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsFileDocumentSource"] class BetaManagedAgentsFileDocumentSource(BaseModel): """Document referenced by file ID.""" file_id: str """ID of a previously uploaded file.""" type: Literal["file"] beta_managed_agents_file_document_source_param.py000066400000000000000000000007171523216435200363340ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsFileDocumentSourceParam"] class BetaManagedAgentsFileDocumentSourceParam(TypedDict, total=False): """Document referenced by file ID.""" file_id: Required[str] """ID of a previously uploaded file.""" type: Required[Literal["file"]] beta_managed_agents_file_image_source.py000066400000000000000000000006051523216435200344140ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsFileImageSource"] class BetaManagedAgentsFileImageSource(BaseModel): """Image referenced by file ID.""" file_id: str """ID of a previously uploaded file.""" type: Literal["file"] beta_managed_agents_file_image_source_param.py000066400000000000000000000007061523216435200355760ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsFileImageSourceParam"] class BetaManagedAgentsFileImageSourceParam(TypedDict, total=False): """Image referenced by file ID.""" file_id: Required[str] """ID of a previously uploaded file.""" type: Required[Literal["file"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions/beta_managed_agents_file_resource.py000066400000000000000000000007611523216435200336630ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from datetime import datetime from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsFileResource"] class BetaManagedAgentsFileResource(BaseModel): id: str created_at: datetime """A timestamp in RFC 3339 format""" file_id: str mount_path: str type: Literal["file"] updated_at: datetime """A timestamp in RFC 3339 format""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions/beta_managed_agents_file_rubric.py000066400000000000000000000006131523216435200333160ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsFileRubric"] class BetaManagedAgentsFileRubric(BaseModel): """Rubric referenced by a file uploaded via the Files API.""" file_id: str """ID of the rubric file.""" type: Literal["file"] beta_managed_agents_file_rubric_params.py000066400000000000000000000007161523216435200346060ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsFileRubricParams"] class BetaManagedAgentsFileRubricParams(TypedDict, total=False): """Rubric referenced by a file uploaded via the Files API.""" file_id: Required[str] """ID of the rubric file.""" type: Required[Literal["file"]] beta_managed_agents_github_repository_resource.py000066400000000000000000000017421523216435200364460ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union, Optional from datetime import datetime from typing_extensions import Literal, Annotated, TypeAlias from ...._utils import PropertyInfo from ...._models import BaseModel from ..beta_managed_agents_branch_checkout import BetaManagedAgentsBranchCheckout from ..beta_managed_agents_commit_checkout import BetaManagedAgentsCommitCheckout __all__ = ["BetaManagedAgentsGitHubRepositoryResource", "Checkout"] Checkout: TypeAlias = Annotated[ Union[BetaManagedAgentsBranchCheckout, BetaManagedAgentsCommitCheckout, None], PropertyInfo(discriminator="type") ] class BetaManagedAgentsGitHubRepositoryResource(BaseModel): id: str created_at: datetime """A timestamp in RFC 3339 format""" mount_path: str type: Literal["github_repository"] updated_at: datetime """A timestamp in RFC 3339 format""" url: str checkout: Optional[Checkout] = None anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions/beta_managed_agents_image_block.py000066400000000000000000000017111523216435200332650ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Literal, Annotated, TypeAlias from ...._utils import PropertyInfo from ...._models import BaseModel from .beta_managed_agents_url_image_source import BetaManagedAgentsURLImageSource from .beta_managed_agents_file_image_source import BetaManagedAgentsFileImageSource from .beta_managed_agents_base64_image_source import BetaManagedAgentsBase64ImageSource __all__ = ["BetaManagedAgentsImageBlock", "Source"] Source: TypeAlias = Annotated[ Union[BetaManagedAgentsBase64ImageSource, BetaManagedAgentsURLImageSource, BetaManagedAgentsFileImageSource], PropertyInfo(discriminator="type"), ] class BetaManagedAgentsImageBlock(BaseModel): """Image content specified directly as base64 data or as a reference via a URL.""" source: Source """Union type for image source variants.""" type: Literal["image"] beta_managed_agents_image_block_param.py000066400000000000000000000017301523216435200343670ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from typing_extensions import Literal, Required, TypeAlias, TypedDict from .beta_managed_agents_url_image_source_param import BetaManagedAgentsURLImageSourceParam from .beta_managed_agents_file_image_source_param import BetaManagedAgentsFileImageSourceParam from .beta_managed_agents_base64_image_source_param import BetaManagedAgentsBase64ImageSourceParam __all__ = ["BetaManagedAgentsImageBlockParam", "Source"] Source: TypeAlias = Union[ BetaManagedAgentsBase64ImageSourceParam, BetaManagedAgentsURLImageSourceParam, BetaManagedAgentsFileImageSourceParam ] class BetaManagedAgentsImageBlockParam(TypedDict, total=False): """Image content specified directly as base64 data or as a reference via a URL.""" source: Required[Source] """Union type for image source variants.""" type: Required[Literal["image"]] beta_managed_agents_mcp_authentication_failed_error.py000066400000000000000000000023431523216435200373470ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Literal, Annotated, TypeAlias from ...._utils import PropertyInfo from ...._models import BaseModel from .beta_managed_agents_retry_status_retrying import BetaManagedAgentsRetryStatusRetrying from .beta_managed_agents_retry_status_terminal import BetaManagedAgentsRetryStatusTerminal from .beta_managed_agents_retry_status_exhausted import BetaManagedAgentsRetryStatusExhausted __all__ = ["BetaManagedAgentsMCPAuthenticationFailedError", "RetryStatus"] RetryStatus: TypeAlias = Annotated[ Union[ BetaManagedAgentsRetryStatusRetrying, BetaManagedAgentsRetryStatusExhausted, BetaManagedAgentsRetryStatusTerminal, ], PropertyInfo(discriminator="type"), ] class BetaManagedAgentsMCPAuthenticationFailedError(BaseModel): """Authentication to an MCP server failed.""" mcp_server_name: str """Name of the MCP server that failed authentication.""" message: str """Human-readable error description.""" retry_status: RetryStatus """What the client should do next in response to this error.""" type: Literal["mcp_authentication_failed_error"] beta_managed_agents_mcp_connection_failed_error.py000066400000000000000000000023171523216435200364700ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Literal, Annotated, TypeAlias from ...._utils import PropertyInfo from ...._models import BaseModel from .beta_managed_agents_retry_status_retrying import BetaManagedAgentsRetryStatusRetrying from .beta_managed_agents_retry_status_terminal import BetaManagedAgentsRetryStatusTerminal from .beta_managed_agents_retry_status_exhausted import BetaManagedAgentsRetryStatusExhausted __all__ = ["BetaManagedAgentsMCPConnectionFailedError", "RetryStatus"] RetryStatus: TypeAlias = Annotated[ Union[ BetaManagedAgentsRetryStatusRetrying, BetaManagedAgentsRetryStatusExhausted, BetaManagedAgentsRetryStatusTerminal, ], PropertyInfo(discriminator="type"), ] class BetaManagedAgentsMCPConnectionFailedError(BaseModel): """Failed to connect to an MCP server.""" mcp_server_name: str """Name of the MCP server that failed to connect.""" message: str """Human-readable error description.""" retry_status: RetryStatus """What the client should do next in response to this error.""" type: Literal["mcp_connection_failed_error"] beta_managed_agents_memory_store_resource.py000066400000000000000000000026401523216435200354070ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsMemoryStoreResource"] class BetaManagedAgentsMemoryStoreResource(BaseModel): """A memory store attached to an agent session.""" memory_store_id: str """The memory store ID (memstore\\__...). Must belong to the caller's organization and workspace. """ type: Literal["memory_store"] access: Optional[Literal["read_write", "read_only"]] = None """Access mode for an attached memory store.""" description: Optional[str] = None """Description of the memory store, snapshotted at attach time. Rendered into the agent's system prompt. Empty string when the store has no description. """ instructions: Optional[str] = None """Per-attachment guidance for the agent on how to use this store. Rendered into the memory section of the system prompt. Max 4096 chars. """ mount_path: Optional[str] = None """Filesystem path where the store is mounted in the session container, e.g. /mnt/memory/user-preferences. Derived from the store's name. Output-only. """ name: Optional[str] = None """Display name of the memory store, snapshotted at attach time. Later edits to the store's name do not propagate to this resource. """ beta_managed_agents_model_overloaded_error.py000066400000000000000000000022471523216435200354740ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Literal, Annotated, TypeAlias from ...._utils import PropertyInfo from ...._models import BaseModel from .beta_managed_agents_retry_status_retrying import BetaManagedAgentsRetryStatusRetrying from .beta_managed_agents_retry_status_terminal import BetaManagedAgentsRetryStatusTerminal from .beta_managed_agents_retry_status_exhausted import BetaManagedAgentsRetryStatusExhausted __all__ = ["BetaManagedAgentsModelOverloadedError", "RetryStatus"] RetryStatus: TypeAlias = Annotated[ Union[ BetaManagedAgentsRetryStatusRetrying, BetaManagedAgentsRetryStatusExhausted, BetaManagedAgentsRetryStatusTerminal, ], PropertyInfo(discriminator="type"), ] class BetaManagedAgentsModelOverloadedError(BaseModel): """The model is currently overloaded. Emitted after automatic retries are exhausted. """ message: str """Human-readable error description.""" retry_status: RetryStatus """What the client should do next in response to this error.""" type: Literal["model_overloaded_error"] beta_managed_agents_model_rate_limited_error.py000066400000000000000000000021631523216435200360070ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Literal, Annotated, TypeAlias from ...._utils import PropertyInfo from ...._models import BaseModel from .beta_managed_agents_retry_status_retrying import BetaManagedAgentsRetryStatusRetrying from .beta_managed_agents_retry_status_terminal import BetaManagedAgentsRetryStatusTerminal from .beta_managed_agents_retry_status_exhausted import BetaManagedAgentsRetryStatusExhausted __all__ = ["BetaManagedAgentsModelRateLimitedError", "RetryStatus"] RetryStatus: TypeAlias = Annotated[ Union[ BetaManagedAgentsRetryStatusRetrying, BetaManagedAgentsRetryStatusExhausted, BetaManagedAgentsRetryStatusTerminal, ], PropertyInfo(discriminator="type"), ] class BetaManagedAgentsModelRateLimitedError(BaseModel): """The model request was rate-limited.""" message: str """Human-readable error description.""" retry_status: RetryStatus """What the client should do next in response to this error.""" type: Literal["model_rate_limited_error"] beta_managed_agents_model_request_failed_error.py000066400000000000000000000022371523216435200363430ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Literal, Annotated, TypeAlias from ...._utils import PropertyInfo from ...._models import BaseModel from .beta_managed_agents_retry_status_retrying import BetaManagedAgentsRetryStatusRetrying from .beta_managed_agents_retry_status_terminal import BetaManagedAgentsRetryStatusTerminal from .beta_managed_agents_retry_status_exhausted import BetaManagedAgentsRetryStatusExhausted __all__ = ["BetaManagedAgentsModelRequestFailedError", "RetryStatus"] RetryStatus: TypeAlias = Annotated[ Union[ BetaManagedAgentsRetryStatusRetrying, BetaManagedAgentsRetryStatusExhausted, BetaManagedAgentsRetryStatusTerminal, ], PropertyInfo(discriminator="type"), ] class BetaManagedAgentsModelRequestFailedError(BaseModel): """A model request failed for a reason other than overload or rate-limiting.""" message: str """Human-readable error description.""" retry_status: RetryStatus """What the client should do next in response to this error.""" type: Literal["model_request_failed_error"] beta_managed_agents_plain_text_document_source.py000066400000000000000000000007561523216435200364070ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsPlainTextDocumentSource"] class BetaManagedAgentsPlainTextDocumentSource(BaseModel): """Plain text document content.""" data: str """The plain text content.""" media_type: Literal["text/plain"] """MIME type of the text content. Must be "text/plain".""" type: Literal["text"] beta_managed_agents_plain_text_document_source_param.py000066400000000000000000000010711523216435200375560ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsPlainTextDocumentSourceParam"] class BetaManagedAgentsPlainTextDocumentSourceParam(TypedDict, total=False): """Plain text document content.""" data: Required[str] """The plain text content.""" media_type: Required[Literal["text/plain"]] """MIME type of the text content. Must be "text/plain".""" type: Required[Literal["text"]] beta_managed_agents_retry_status_exhausted.py000066400000000000000000000006571523216435200356040ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsRetryStatusExhausted"] class BetaManagedAgentsRetryStatusExhausted(BaseModel): """This turn is dead; queued inputs are flushed and the session returns to idle. Client may send a new prompt. """ type: Literal["exhausted"] beta_managed_agents_retry_status_retrying.py000066400000000000000000000007411523216435200354470ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsRetryStatusRetrying"] class BetaManagedAgentsRetryStatusRetrying(BaseModel): """The server is retrying automatically. Client should wait; the same error type may fire again as retrying, then once as exhausted when the retry budget runs out. """ type: Literal["retrying"] beta_managed_agents_retry_status_terminal.py000066400000000000000000000006241523216435200354170ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsRetryStatusTerminal"] class BetaManagedAgentsRetryStatusTerminal(BaseModel): """ The session encountered a terminal error and will transition to `terminated` state. """ type: Literal["terminal"] beta_managed_agents_search_result_block.py000066400000000000000000000016151523216435200347720ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List from typing_extensions import Literal from ...._models import BaseModel from .beta_managed_agents_search_result_content import BetaManagedAgentsSearchResultContent from .beta_managed_agents_search_result_citations import BetaManagedAgentsSearchResultCitations __all__ = ["BetaManagedAgentsSearchResultBlock"] class BetaManagedAgentsSearchResultBlock(BaseModel): """A block containing a web search result.""" citations: BetaManagedAgentsSearchResultCitations """Citation settings for a search result.""" content: List[BetaManagedAgentsSearchResultContent] """Array of text content blocks from the search result.""" source: str """The URL source of the search result.""" title: str """The title of the search result.""" type: Literal["search_result"] beta_managed_agents_search_result_block_param.py000066400000000000000000000020251523216435200361460ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Iterable from typing_extensions import Literal, Required, TypedDict from .beta_managed_agents_search_result_content_param import BetaManagedAgentsSearchResultContentParam from .beta_managed_agents_search_result_citations_param import BetaManagedAgentsSearchResultCitationsParam __all__ = ["BetaManagedAgentsSearchResultBlockParam"] class BetaManagedAgentsSearchResultBlockParam(TypedDict, total=False): """A block containing a web search result.""" citations: Required[BetaManagedAgentsSearchResultCitationsParam] """Citation settings for a search result.""" content: Required[Iterable[BetaManagedAgentsSearchResultContentParam]] """Array of text content blocks from the search result.""" source: Required[str] """The URL source of the search result.""" title: Required[str] """The title of the search result.""" type: Required[Literal["search_result"]] beta_managed_agents_search_result_citations.py000066400000000000000000000005561523216435200357000ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from ...._models import BaseModel __all__ = ["BetaManagedAgentsSearchResultCitations"] class BetaManagedAgentsSearchResultCitations(BaseModel): """Citation settings for a search result.""" enabled: bool """Whether citations are enabled for this search result.""" beta_managed_agents_search_result_citations_param.py000066400000000000000000000007031523216435200370520ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Required, TypedDict __all__ = ["BetaManagedAgentsSearchResultCitationsParam"] class BetaManagedAgentsSearchResultCitationsParam(TypedDict, total=False): """Citation settings for a search result.""" enabled: Required[bool] """Whether citations are enabled for this search result.""" beta_managed_agents_search_result_content.py000066400000000000000000000006021523216435200353450ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsSearchResultContent"] class BetaManagedAgentsSearchResultContent(BaseModel): """Text content within a search result.""" text: str """The text content.""" type: Literal["text"] beta_managed_agents_search_result_content_param.py000066400000000000000000000007031523216435200365270ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsSearchResultContentParam"] class BetaManagedAgentsSearchResultContentParam(TypedDict, total=False): """Text content within a search result.""" text: Required[str] """The text content.""" type: Required[Literal["text"]] beta_managed_agents_send_session_events.py000066400000000000000000000030521523216435200350320ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Union, Optional from typing_extensions import Annotated, TypeAlias from ...._utils import PropertyInfo from ...._models import BaseModel from .beta_managed_agents_user_message_event import BetaManagedAgentsUserMessageEvent from .beta_managed_agents_user_interrupt_event import BetaManagedAgentsUserInterruptEvent from ..beta_managed_agents_system_message_event import BetaManagedAgentsSystemMessageEvent from ..beta_managed_agents_user_tool_result_event import BetaManagedAgentsUserToolResultEvent from .beta_managed_agents_user_define_outcome_event import BetaManagedAgentsUserDefineOutcomeEvent from .beta_managed_agents_user_tool_confirmation_event import BetaManagedAgentsUserToolConfirmationEvent from .beta_managed_agents_user_custom_tool_result_event import BetaManagedAgentsUserCustomToolResultEvent __all__ = ["BetaManagedAgentsSendSessionEvents", "Data"] Data: TypeAlias = Annotated[ Union[ BetaManagedAgentsUserMessageEvent, BetaManagedAgentsUserInterruptEvent, BetaManagedAgentsUserToolConfirmationEvent, BetaManagedAgentsUserCustomToolResultEvent, BetaManagedAgentsUserDefineOutcomeEvent, BetaManagedAgentsUserToolResultEvent, BetaManagedAgentsSystemMessageEvent, ], PropertyInfo(discriminator="type"), ] class BetaManagedAgentsSendSessionEvents(BaseModel): """Events that were successfully sent to the session.""" data: Optional[List[Data]] = None """Sent events""" beta_managed_agents_session_deleted_event.py000066400000000000000000000011471523216435200353270ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from datetime import datetime from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsSessionDeletedEvent"] class BetaManagedAgentsSessionDeletedEvent(BaseModel): """Emitted when a session has been deleted. Terminates any active event stream — no further events will be emitted for this session. """ id: str """Unique identifier for this event.""" processed_at: datetime """A timestamp in RFC 3339 format""" type: Literal["session.deleted"] beta_managed_agents_session_end_turn.py000066400000000000000000000005731523216435200343400ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsSessionEndTurn"] class BetaManagedAgentsSessionEndTurn(BaseModel): """The agent completed its turn naturally and is ready for the next user message.""" type: Literal["end_turn"] beta_managed_agents_session_error_event.py000066400000000000000000000040711523216435200350510ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from datetime import datetime from typing_extensions import Literal, Annotated, TypeAlias from ...._utils import PropertyInfo from ...._models import BaseModel from .beta_managed_agents_billing_error import BetaManagedAgentsBillingError from .beta_managed_agents_unknown_error import BetaManagedAgentsUnknownError from .beta_managed_agents_model_overloaded_error import BetaManagedAgentsModelOverloadedError from .beta_managed_agents_model_rate_limited_error import BetaManagedAgentsModelRateLimitedError from .beta_managed_agents_model_request_failed_error import BetaManagedAgentsModelRequestFailedError from .beta_managed_agents_mcp_connection_failed_error import BetaManagedAgentsMCPConnectionFailedError from .beta_managed_agents_mcp_authentication_failed_error import BetaManagedAgentsMCPAuthenticationFailedError from .beta_managed_agents_credential_host_unreachable_error import BetaManagedAgentsCredentialHostUnreachableError __all__ = ["BetaManagedAgentsSessionErrorEvent", "Error"] Error: TypeAlias = Annotated[ Union[ BetaManagedAgentsUnknownError, BetaManagedAgentsModelOverloadedError, BetaManagedAgentsModelRateLimitedError, BetaManagedAgentsModelRequestFailedError, BetaManagedAgentsMCPConnectionFailedError, BetaManagedAgentsMCPAuthenticationFailedError, BetaManagedAgentsBillingError, BetaManagedAgentsCredentialHostUnreachableError, ], PropertyInfo(discriminator="type"), ] class BetaManagedAgentsSessionErrorEvent(BaseModel): """An error event indicating a problem occurred during session execution.""" id: str """Unique identifier for this event.""" error: Error """An unknown or unexpected error occurred during session execution. A fallback variant; clients that don't recognize a new error code can match on `retry_status` and `message` alone. """ processed_at: datetime """A timestamp in RFC 3339 format""" type: Literal["session.error"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions/beta_managed_agents_session_event.py000066400000000000000000000130401523216435200337130ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from ...._utils import PropertyInfo from .beta_managed_agents_user_message_event import BetaManagedAgentsUserMessageEvent from .beta_managed_agents_agent_message_event import BetaManagedAgentsAgentMessageEvent from .beta_managed_agents_session_error_event import BetaManagedAgentsSessionErrorEvent from .beta_managed_agents_agent_thinking_event import BetaManagedAgentsAgentThinkingEvent from .beta_managed_agents_agent_tool_use_event import BetaManagedAgentsAgentToolUseEvent from .beta_managed_agents_user_interrupt_event import BetaManagedAgentsUserInterruptEvent from ..beta_managed_agents_system_message_event import BetaManagedAgentsSystemMessageEvent from .beta_managed_agents_session_deleted_event import BetaManagedAgentsSessionDeletedEvent from ..beta_managed_agents_session_updated_event import BetaManagedAgentsSessionUpdatedEvent from ..beta_managed_agents_user_tool_result_event import BetaManagedAgentsUserToolResultEvent from .beta_managed_agents_agent_tool_result_event import BetaManagedAgentsAgentToolResultEvent from .beta_managed_agents_agent_mcp_tool_use_event import BetaManagedAgentsAgentMCPToolUseEvent from .beta_managed_agents_session_status_idle_event import BetaManagedAgentsSessionStatusIdleEvent from .beta_managed_agents_user_define_outcome_event import BetaManagedAgentsUserDefineOutcomeEvent from .beta_managed_agents_agent_custom_tool_use_event import BetaManagedAgentsAgentCustomToolUseEvent from .beta_managed_agents_agent_mcp_tool_result_event import BetaManagedAgentsAgentMCPToolResultEvent from .beta_managed_agents_session_status_running_event import BetaManagedAgentsSessionStatusRunningEvent from .beta_managed_agents_session_thread_created_event import BetaManagedAgentsSessionThreadCreatedEvent from .beta_managed_agents_span_model_request_end_event import BetaManagedAgentsSpanModelRequestEndEvent from .beta_managed_agents_user_tool_confirmation_event import BetaManagedAgentsUserToolConfirmationEvent from .beta_managed_agents_user_custom_tool_result_event import BetaManagedAgentsUserCustomToolResultEvent from .beta_managed_agents_span_model_request_start_event import BetaManagedAgentsSpanModelRequestStartEvent from .beta_managed_agents_agent_thread_message_sent_event import BetaManagedAgentsAgentThreadMessageSentEvent from .beta_managed_agents_session_status_terminated_event import BetaManagedAgentsSessionStatusTerminatedEvent from .beta_managed_agents_session_status_rescheduled_event import BetaManagedAgentsSessionStatusRescheduledEvent from .beta_managed_agents_session_thread_status_idle_event import BetaManagedAgentsSessionThreadStatusIdleEvent from .beta_managed_agents_span_outcome_evaluation_end_event import BetaManagedAgentsSpanOutcomeEvaluationEndEvent from .beta_managed_agents_agent_thread_message_received_event import BetaManagedAgentsAgentThreadMessageReceivedEvent from .beta_managed_agents_session_thread_status_running_event import BetaManagedAgentsSessionThreadStatusRunningEvent from .beta_managed_agents_span_outcome_evaluation_start_event import BetaManagedAgentsSpanOutcomeEvaluationStartEvent from .beta_managed_agents_agent_thread_context_compacted_event import BetaManagedAgentsAgentThreadContextCompactedEvent from .beta_managed_agents_span_outcome_evaluation_ongoing_event import ( BetaManagedAgentsSpanOutcomeEvaluationOngoingEvent, ) from .beta_managed_agents_session_thread_status_terminated_event import ( BetaManagedAgentsSessionThreadStatusTerminatedEvent, ) from .beta_managed_agents_session_thread_status_rescheduled_event import ( BetaManagedAgentsSessionThreadStatusRescheduledEvent, ) __all__ = ["BetaManagedAgentsSessionEvent"] BetaManagedAgentsSessionEvent: TypeAlias = Annotated[ Union[ BetaManagedAgentsUserMessageEvent, BetaManagedAgentsUserInterruptEvent, BetaManagedAgentsUserToolConfirmationEvent, BetaManagedAgentsUserCustomToolResultEvent, BetaManagedAgentsAgentCustomToolUseEvent, BetaManagedAgentsAgentMessageEvent, BetaManagedAgentsAgentThinkingEvent, BetaManagedAgentsAgentMCPToolUseEvent, BetaManagedAgentsAgentMCPToolResultEvent, BetaManagedAgentsAgentToolUseEvent, BetaManagedAgentsAgentToolResultEvent, BetaManagedAgentsAgentThreadMessageReceivedEvent, BetaManagedAgentsAgentThreadMessageSentEvent, BetaManagedAgentsAgentThreadContextCompactedEvent, BetaManagedAgentsSessionErrorEvent, BetaManagedAgentsSessionStatusRescheduledEvent, BetaManagedAgentsSessionStatusRunningEvent, BetaManagedAgentsSessionStatusIdleEvent, BetaManagedAgentsSessionStatusTerminatedEvent, BetaManagedAgentsSessionThreadCreatedEvent, BetaManagedAgentsSpanOutcomeEvaluationStartEvent, BetaManagedAgentsSpanOutcomeEvaluationEndEvent, BetaManagedAgentsSpanModelRequestStartEvent, BetaManagedAgentsSpanModelRequestEndEvent, BetaManagedAgentsSpanOutcomeEvaluationOngoingEvent, BetaManagedAgentsUserDefineOutcomeEvent, BetaManagedAgentsSessionDeletedEvent, BetaManagedAgentsSessionThreadStatusRunningEvent, BetaManagedAgentsSessionThreadStatusIdleEvent, BetaManagedAgentsSessionThreadStatusTerminatedEvent, BetaManagedAgentsUserToolResultEvent, BetaManagedAgentsSessionThreadStatusRescheduledEvent, BetaManagedAgentsSessionUpdatedEvent, BetaManagedAgentsSystemMessageEvent, ], PropertyInfo(discriminator="type"), ] beta_managed_agents_session_requires_action.py000066400000000000000000000012721523216435200357130ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsSessionRequiresAction"] class BetaManagedAgentsSessionRequiresAction(BaseModel): """ The agent is idle waiting on one or more blocking user-input events (tool confirmation, custom tool result, etc.). Resolving all of them transitions the session back to running. """ event_ids: List[str] """The ids of events the agent is blocked on. Resolving fewer than all re-emits `session.status_idle` with the remainder. """ type: Literal["requires_action"] beta_managed_agents_session_resource.py000066400000000000000000000013641523216435200343500ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from ...._utils import PropertyInfo from .beta_managed_agents_file_resource import BetaManagedAgentsFileResource from .beta_managed_agents_memory_store_resource import BetaManagedAgentsMemoryStoreResource from .beta_managed_agents_github_repository_resource import BetaManagedAgentsGitHubRepositoryResource __all__ = ["BetaManagedAgentsSessionResource"] BetaManagedAgentsSessionResource: TypeAlias = Annotated[ Union[ BetaManagedAgentsGitHubRepositoryResource, BetaManagedAgentsFileResource, BetaManagedAgentsMemoryStoreResource ], PropertyInfo(discriminator="type"), ] beta_managed_agents_session_retries_exhausted.py000066400000000000000000000007111523216435200362430ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsSessionRetriesExhausted"] class BetaManagedAgentsSessionRetriesExhausted(BaseModel): """ The turn ended because repeated errors exhausted the retry budget or an error escalated to `retry_status: 'exhausted'`. """ type: Literal["retries_exhausted"] beta_managed_agents_session_status_idle_event.py000066400000000000000000000023661523216435200362450ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from datetime import datetime from typing_extensions import Literal, Annotated, TypeAlias from ...._utils import PropertyInfo from ...._models import BaseModel from .beta_managed_agents_session_end_turn import BetaManagedAgentsSessionEndTurn from .beta_managed_agents_session_requires_action import BetaManagedAgentsSessionRequiresAction from .beta_managed_agents_session_retries_exhausted import BetaManagedAgentsSessionRetriesExhausted __all__ = ["BetaManagedAgentsSessionStatusIdleEvent", "StopReason"] StopReason: TypeAlias = Annotated[ Union[ BetaManagedAgentsSessionEndTurn, BetaManagedAgentsSessionRequiresAction, BetaManagedAgentsSessionRetriesExhausted, ], PropertyInfo(discriminator="type"), ] class BetaManagedAgentsSessionStatusIdleEvent(BaseModel): """Indicates the agent has paused and is awaiting user input.""" id: str """Unique identifier for this event.""" processed_at: datetime """A timestamp in RFC 3339 format""" stop_reason: StopReason """The agent completed its turn naturally and is ready for the next user message.""" type: Literal["session.status_idle"] beta_managed_agents_session_status_rescheduled_event.py000066400000000000000000000011341523216435200376070ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from datetime import datetime from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsSessionStatusRescheduledEvent"] class BetaManagedAgentsSessionStatusRescheduledEvent(BaseModel): """ Indicates the session is recovering from an error state and is rescheduled for execution. """ id: str """Unique identifier for this event.""" processed_at: datetime """A timestamp in RFC 3339 format""" type: Literal["session.status_rescheduled"] beta_managed_agents_session_status_running_event.py000066400000000000000000000010601523216435200367760ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from datetime import datetime from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsSessionStatusRunningEvent"] class BetaManagedAgentsSessionStatusRunningEvent(BaseModel): """Indicates the session is actively running and the agent is working.""" id: str """Unique identifier for this event.""" processed_at: datetime """A timestamp in RFC 3339 format""" type: Literal["session.status_running"] beta_managed_agents_session_status_terminated_event.py000066400000000000000000000011011523216435200374460ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from datetime import datetime from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsSessionStatusTerminatedEvent"] class BetaManagedAgentsSessionStatusTerminatedEvent(BaseModel): """Indicates the session has terminated, either due to an error or completion.""" id: str """Unique identifier for this event.""" processed_at: datetime """A timestamp in RFC 3339 format""" type: Literal["session.status_terminated"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions/beta_managed_agents_session_thread.py000066400000000000000000000036071523216435200340510ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from datetime import datetime from typing_extensions import Literal from ...._models import BaseModel from .beta_managed_agents_session_thread_stats import BetaManagedAgentsSessionThreadStats from .beta_managed_agents_session_thread_usage import BetaManagedAgentsSessionThreadUsage from ..beta_managed_agents_session_thread_agent import BetaManagedAgentsSessionThreadAgent from .beta_managed_agents_session_thread_status import BetaManagedAgentsSessionThreadStatus __all__ = ["BetaManagedAgentsSessionThread"] class BetaManagedAgentsSessionThread(BaseModel): """An execution thread within a `session`. Each session has one primary thread plus zero or more child threads spawned by the coordinator. """ id: str """Unique identifier for this thread.""" agent: BetaManagedAgentsSessionThreadAgent """Resolved `agent` definition for a single `session_thread`. Snapshot of the agent at thread creation time. The multiagent roster is not repeated here; read it from `Session.agent`. """ archived_at: Optional[datetime] = None """A timestamp in RFC 3339 format""" created_at: datetime """A timestamp in RFC 3339 format""" parent_thread_id: Optional[str] = None """Parent thread that spawned this thread. Null for the primary thread.""" session_id: str """The session this thread belongs to.""" stats: Optional[BetaManagedAgentsSessionThreadStats] = None """Timing statistics for a session thread.""" status: BetaManagedAgentsSessionThreadStatus """SessionThreadStatus enum""" type: Literal["session_thread"] updated_at: datetime """A timestamp in RFC 3339 format""" usage: Optional[BetaManagedAgentsSessionThreadUsage] = None """Cumulative token usage for a session thread across all turns.""" beta_managed_agents_session_thread_created_event.py000066400000000000000000000014541523216435200366600ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from datetime import datetime from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsSessionThreadCreatedEvent"] class BetaManagedAgentsSessionThreadCreatedEvent(BaseModel): """Emitted when a subagent is spawned as a new thread. Written to the parent thread's output stream so clients observing the session see child creation. """ id: str """Unique identifier for this event.""" agent_name: str """Name of the callable agent the thread runs.""" processed_at: datetime """A timestamp in RFC 3339 format""" session_thread_id: str """Public `sthr_` ID of the newly created thread.""" type: Literal["session.thread_created"] beta_managed_agents_session_thread_stats.py000066400000000000000000000014131523216435200352010ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from ...._models import BaseModel __all__ = ["BetaManagedAgentsSessionThreadStats"] class BetaManagedAgentsSessionThreadStats(BaseModel): """Timing statistics for a session thread.""" active_seconds: Optional[float] = None """Cumulative time in seconds the thread spent actively running. Excludes idle time. """ duration_seconds: Optional[float] = None """Elapsed time since thread creation in seconds. For archived threads, frozen at the final update. """ startup_seconds: Optional[float] = None """Time in seconds for the thread to begin running. Zero for child threads, which start immediately. """ beta_managed_agents_session_thread_status.py000066400000000000000000000004501523216435200353660ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal, TypeAlias __all__ = ["BetaManagedAgentsSessionThreadStatus"] BetaManagedAgentsSessionThreadStatus: TypeAlias = Literal["running", "idle", "rescheduling", "terminated"] beta_managed_agents_session_thread_status_idle_event.py000066400000000000000000000030021523216435200375600ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from datetime import datetime from typing_extensions import Literal, Annotated, TypeAlias from ...._utils import PropertyInfo from ...._models import BaseModel from .beta_managed_agents_session_end_turn import BetaManagedAgentsSessionEndTurn from .beta_managed_agents_session_requires_action import BetaManagedAgentsSessionRequiresAction from .beta_managed_agents_session_retries_exhausted import BetaManagedAgentsSessionRetriesExhausted __all__ = ["BetaManagedAgentsSessionThreadStatusIdleEvent", "StopReason"] StopReason: TypeAlias = Annotated[ Union[ BetaManagedAgentsSessionEndTurn, BetaManagedAgentsSessionRequiresAction, BetaManagedAgentsSessionRetriesExhausted, ], PropertyInfo(discriminator="type"), ] class BetaManagedAgentsSessionThreadStatusIdleEvent(BaseModel): """A session thread has yielded and is awaiting input. Emitted on the thread's own stream and cross-posted to the primary stream for child threads. """ id: str """Unique identifier for this event.""" agent_name: str """Name of the agent the thread runs.""" processed_at: datetime """A timestamp in RFC 3339 format""" session_thread_id: str """Public sthr\\__ ID of the thread that went idle.""" stop_reason: StopReason """The agent completed its turn naturally and is ready for the next user message.""" type: Literal["session.thread_status_idle"] beta_managed_agents_session_thread_status_rescheduled_event.py000066400000000000000000000015231523216435200411400ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from datetime import datetime from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsSessionThreadStatusRescheduledEvent"] class BetaManagedAgentsSessionThreadStatusRescheduledEvent(BaseModel): """A session thread hit a transient error and is retrying automatically. Emitted on the thread's own stream and cross-posted to the primary stream for child threads. """ id: str """Unique identifier for this event.""" agent_name: str """Name of the agent the thread runs.""" processed_at: datetime """A timestamp in RFC 3339 format""" session_thread_id: str """Public sthr\\__ ID of the thread that is retrying.""" type: Literal["session.thread_status_rescheduled"] beta_managed_agents_session_thread_status_running_event.py000066400000000000000000000014531523216435200403330ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from datetime import datetime from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsSessionThreadStatusRunningEvent"] class BetaManagedAgentsSessionThreadStatusRunningEvent(BaseModel): """A session thread has begun executing. Emitted on the thread's own stream and cross-posted to the primary stream for child threads. """ id: str """Unique identifier for this event.""" agent_name: str """Name of the agent the thread runs.""" processed_at: datetime """A timestamp in RFC 3339 format""" session_thread_id: str """Public sthr\\__ ID of the thread that started running.""" type: Literal["session.thread_status_running"] beta_managed_agents_session_thread_status_terminated_event.py000066400000000000000000000015131523216435200410040ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from datetime import datetime from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsSessionThreadStatusTerminatedEvent"] class BetaManagedAgentsSessionThreadStatusTerminatedEvent(BaseModel): """A session thread has terminated and will accept no further input. Emitted on the thread's own stream and cross-posted to the primary stream for child threads. """ id: str """Unique identifier for this event.""" agent_name: str """Name of the agent the thread runs.""" processed_at: datetime """A timestamp in RFC 3339 format""" session_thread_id: str """Public sthr\\__ ID of the thread that terminated.""" type: Literal["session.thread_status_terminated"] beta_managed_agents_session_thread_usage.py000066400000000000000000000015361523216435200351550ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from ...._models import BaseModel from ..beta_managed_agents_cache_creation_usage import BetaManagedAgentsCacheCreationUsage __all__ = ["BetaManagedAgentsSessionThreadUsage"] class BetaManagedAgentsSessionThreadUsage(BaseModel): """Cumulative token usage for a session thread across all turns.""" cache_creation: Optional[BetaManagedAgentsCacheCreationUsage] = None """Prompt-cache creation token usage broken down by cache lifetime.""" cache_read_input_tokens: Optional[int] = None """Total tokens read from prompt cache.""" input_tokens: Optional[int] = None """Total input tokens consumed across all turns.""" output_tokens: Optional[int] = None """Total output tokens generated across all turns.""" beta_managed_agents_span_model_request_end_event.py000066400000000000000000000021431523216435200366720ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from datetime import datetime from typing_extensions import Literal from ...._compat import PYDANTIC_V1, ConfigDict from ...._models import BaseModel from .beta_managed_agents_span_model_usage import BetaManagedAgentsSpanModelUsage __all__ = ["BetaManagedAgentsSpanModelRequestEndEvent"] class BetaManagedAgentsSpanModelRequestEndEvent(BaseModel): """Emitted when a model request completes.""" id: str """Unique identifier for this event.""" is_error: Optional[bool] = None """Whether the model request resulted in an error.""" model_request_start_id: str """The id of the corresponding `span.model_request_start` event.""" model_usage: BetaManagedAgentsSpanModelUsage """Token usage for a single model request.""" processed_at: datetime """A timestamp in RFC 3339 format""" type: Literal["span.model_request_end"] if not PYDANTIC_V1: # allow fields with a `model_` prefix model_config = ConfigDict(protected_namespaces=tuple()) beta_managed_agents_span_model_request_start_event.py000066400000000000000000000010501523216435200372550ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from datetime import datetime from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsSpanModelRequestStartEvent"] class BetaManagedAgentsSpanModelRequestStartEvent(BaseModel): """Emitted when a model request is initiated by the agent.""" id: str """Unique identifier for this event.""" processed_at: datetime """A timestamp in RFC 3339 format""" type: Literal["span.model_request_start"] beta_managed_agents_span_model_usage.py000066400000000000000000000016541523216435200342650ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsSpanModelUsage"] class BetaManagedAgentsSpanModelUsage(BaseModel): """Token usage for a single model request.""" cache_creation_input_tokens: int """Tokens used to create prompt cache in this request.""" cache_read_input_tokens: int """Tokens read from prompt cache in this request.""" input_tokens: int """Input tokens consumed by this request.""" output_tokens: int """Output tokens generated by this request.""" speed: Optional[Literal["standard", "fast"]] = None """Inference speed mode. `fast` provides significantly faster output token generation at premium pricing. Not all models support `fast`; invalid combinations are rejected at create time. """ beta_managed_agents_span_outcome_evaluation_end_event.py000066400000000000000000000036651523216435200377360ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from datetime import datetime from typing_extensions import Literal from ...._models import BaseModel from .beta_managed_agents_span_model_usage import BetaManagedAgentsSpanModelUsage __all__ = ["BetaManagedAgentsSpanOutcomeEvaluationEndEvent"] class BetaManagedAgentsSpanOutcomeEvaluationEndEvent(BaseModel): """Emitted when an outcome evaluation cycle completes. Carries the verdict and aggregate token usage. A verdict of `needs_revision` means another evaluation cycle follows; `satisfied`, `max_iterations_reached`, `failed`, or `interrupted` are terminal — no further evaluation cycles follow. """ id: str """Unique identifier for this event.""" explanation: str """Human-readable explanation of the verdict. For `needs_revision`, describes which criteria failed and why. """ iteration: int """ 0-indexed revision cycle, matching the corresponding `span.outcome_evaluation_start`. """ outcome_evaluation_start_id: str """The id of the corresponding `span.outcome_evaluation_start` event.""" outcome_id: str """The `outc_` ID of the outcome being evaluated.""" processed_at: datetime """A timestamp in RFC 3339 format""" result: str """Evaluation verdict. 'satisfied': criteria met, session goes idle. 'needs_revision': criteria not met, another revision cycle follows. 'max_iterations_reached': evaluation budget exhausted with criteria still unmet — one final acknowledgment turn follows before the session goes idle, but no further evaluation runs. 'failed': grader determined the rubric does not apply to the deliverables. 'interrupted': user sent an interrupt while evaluation was in progress. """ type: Literal["span.outcome_evaluation_end"] usage: BetaManagedAgentsSpanModelUsage """Token usage for a single model request.""" beta_managed_agents_span_outcome_evaluation_ongoing_event.py000066400000000000000000000017321523216435200406210ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from datetime import datetime from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsSpanOutcomeEvaluationOngoingEvent"] class BetaManagedAgentsSpanOutcomeEvaluationOngoingEvent(BaseModel): """Periodic heartbeat emitted while an outcome evaluation cycle is in progress. Distinguishes 'evaluation is actively running' from 'evaluation is stuck' between the corresponding `span.outcome_evaluation_start` and `span.outcome_evaluation_end` events. """ id: str """Unique identifier for this event.""" iteration: int """ 0-indexed revision cycle, matching the corresponding `span.outcome_evaluation_start`. """ outcome_id: str """The `outc_` ID of the outcome being evaluated.""" processed_at: datetime """A timestamp in RFC 3339 format""" type: Literal["span.outcome_evaluation_ongoing"] beta_managed_agents_span_outcome_evaluation_start_event.py000066400000000000000000000014211523216435200403110ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from datetime import datetime from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsSpanOutcomeEvaluationStartEvent"] class BetaManagedAgentsSpanOutcomeEvaluationStartEvent(BaseModel): """Emitted when an outcome evaluation cycle begins.""" id: str """Unique identifier for this event.""" iteration: int """0-indexed revision cycle. 0 is the first evaluation; 1 is the re-evaluation after the first revision; etc. """ outcome_id: str """The `outc_` ID of the outcome being evaluated.""" processed_at: datetime """A timestamp in RFC 3339 format""" type: Literal["span.outcome_evaluation_start"] beta_managed_agents_stream_session_events.py000066400000000000000000000134141523216435200353770ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from ...._utils import PropertyInfo from ..beta_managed_agents_delta_event import BetaManagedAgentsDeltaEvent from ..beta_managed_agents_start_event import BetaManagedAgentsStartEvent from .beta_managed_agents_user_message_event import BetaManagedAgentsUserMessageEvent from .beta_managed_agents_agent_message_event import BetaManagedAgentsAgentMessageEvent from .beta_managed_agents_session_error_event import BetaManagedAgentsSessionErrorEvent from .beta_managed_agents_agent_thinking_event import BetaManagedAgentsAgentThinkingEvent from .beta_managed_agents_agent_tool_use_event import BetaManagedAgentsAgentToolUseEvent from .beta_managed_agents_user_interrupt_event import BetaManagedAgentsUserInterruptEvent from ..beta_managed_agents_system_message_event import BetaManagedAgentsSystemMessageEvent from .beta_managed_agents_session_deleted_event import BetaManagedAgentsSessionDeletedEvent from ..beta_managed_agents_session_updated_event import BetaManagedAgentsSessionUpdatedEvent from ..beta_managed_agents_user_tool_result_event import BetaManagedAgentsUserToolResultEvent from .beta_managed_agents_agent_tool_result_event import BetaManagedAgentsAgentToolResultEvent from .beta_managed_agents_agent_mcp_tool_use_event import BetaManagedAgentsAgentMCPToolUseEvent from .beta_managed_agents_session_status_idle_event import BetaManagedAgentsSessionStatusIdleEvent from .beta_managed_agents_user_define_outcome_event import BetaManagedAgentsUserDefineOutcomeEvent from .beta_managed_agents_agent_custom_tool_use_event import BetaManagedAgentsAgentCustomToolUseEvent from .beta_managed_agents_agent_mcp_tool_result_event import BetaManagedAgentsAgentMCPToolResultEvent from .beta_managed_agents_session_status_running_event import BetaManagedAgentsSessionStatusRunningEvent from .beta_managed_agents_session_thread_created_event import BetaManagedAgentsSessionThreadCreatedEvent from .beta_managed_agents_span_model_request_end_event import BetaManagedAgentsSpanModelRequestEndEvent from .beta_managed_agents_user_tool_confirmation_event import BetaManagedAgentsUserToolConfirmationEvent from .beta_managed_agents_user_custom_tool_result_event import BetaManagedAgentsUserCustomToolResultEvent from .beta_managed_agents_span_model_request_start_event import BetaManagedAgentsSpanModelRequestStartEvent from .beta_managed_agents_agent_thread_message_sent_event import BetaManagedAgentsAgentThreadMessageSentEvent from .beta_managed_agents_session_status_terminated_event import BetaManagedAgentsSessionStatusTerminatedEvent from .beta_managed_agents_session_status_rescheduled_event import BetaManagedAgentsSessionStatusRescheduledEvent from .beta_managed_agents_session_thread_status_idle_event import BetaManagedAgentsSessionThreadStatusIdleEvent from .beta_managed_agents_span_outcome_evaluation_end_event import BetaManagedAgentsSpanOutcomeEvaluationEndEvent from .beta_managed_agents_agent_thread_message_received_event import BetaManagedAgentsAgentThreadMessageReceivedEvent from .beta_managed_agents_session_thread_status_running_event import BetaManagedAgentsSessionThreadStatusRunningEvent from .beta_managed_agents_span_outcome_evaluation_start_event import BetaManagedAgentsSpanOutcomeEvaluationStartEvent from .beta_managed_agents_agent_thread_context_compacted_event import BetaManagedAgentsAgentThreadContextCompactedEvent from .beta_managed_agents_span_outcome_evaluation_ongoing_event import ( BetaManagedAgentsSpanOutcomeEvaluationOngoingEvent, ) from .beta_managed_agents_session_thread_status_terminated_event import ( BetaManagedAgentsSessionThreadStatusTerminatedEvent, ) from .beta_managed_agents_session_thread_status_rescheduled_event import ( BetaManagedAgentsSessionThreadStatusRescheduledEvent, ) __all__ = ["BetaManagedAgentsStreamSessionEvents"] BetaManagedAgentsStreamSessionEvents: TypeAlias = Annotated[ Union[ BetaManagedAgentsUserMessageEvent, BetaManagedAgentsUserInterruptEvent, BetaManagedAgentsUserToolConfirmationEvent, BetaManagedAgentsUserCustomToolResultEvent, BetaManagedAgentsAgentCustomToolUseEvent, BetaManagedAgentsAgentMessageEvent, BetaManagedAgentsAgentThinkingEvent, BetaManagedAgentsAgentMCPToolUseEvent, BetaManagedAgentsAgentMCPToolResultEvent, BetaManagedAgentsAgentToolUseEvent, BetaManagedAgentsAgentToolResultEvent, BetaManagedAgentsAgentThreadMessageReceivedEvent, BetaManagedAgentsAgentThreadMessageSentEvent, BetaManagedAgentsAgentThreadContextCompactedEvent, BetaManagedAgentsSessionErrorEvent, BetaManagedAgentsSessionStatusRescheduledEvent, BetaManagedAgentsSessionStatusRunningEvent, BetaManagedAgentsSessionStatusIdleEvent, BetaManagedAgentsSessionStatusTerminatedEvent, BetaManagedAgentsSessionThreadCreatedEvent, BetaManagedAgentsSpanOutcomeEvaluationStartEvent, BetaManagedAgentsSpanOutcomeEvaluationEndEvent, BetaManagedAgentsSpanModelRequestStartEvent, BetaManagedAgentsSpanModelRequestEndEvent, BetaManagedAgentsSpanOutcomeEvaluationOngoingEvent, BetaManagedAgentsUserDefineOutcomeEvent, BetaManagedAgentsSessionDeletedEvent, BetaManagedAgentsSessionThreadStatusRunningEvent, BetaManagedAgentsSessionThreadStatusIdleEvent, BetaManagedAgentsSessionThreadStatusTerminatedEvent, BetaManagedAgentsUserToolResultEvent, BetaManagedAgentsSessionThreadStatusRescheduledEvent, BetaManagedAgentsSessionUpdatedEvent, BetaManagedAgentsStartEvent, BetaManagedAgentsDeltaEvent, BetaManagedAgentsSystemMessageEvent, ], PropertyInfo(discriminator="type"), ] beta_managed_agents_stream_session_thread_events.py000066400000000000000000000134301523216435200367240ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from ...._utils import PropertyInfo from ..beta_managed_agents_delta_event import BetaManagedAgentsDeltaEvent from ..beta_managed_agents_start_event import BetaManagedAgentsStartEvent from .beta_managed_agents_user_message_event import BetaManagedAgentsUserMessageEvent from .beta_managed_agents_agent_message_event import BetaManagedAgentsAgentMessageEvent from .beta_managed_agents_session_error_event import BetaManagedAgentsSessionErrorEvent from .beta_managed_agents_agent_thinking_event import BetaManagedAgentsAgentThinkingEvent from .beta_managed_agents_agent_tool_use_event import BetaManagedAgentsAgentToolUseEvent from .beta_managed_agents_user_interrupt_event import BetaManagedAgentsUserInterruptEvent from ..beta_managed_agents_system_message_event import BetaManagedAgentsSystemMessageEvent from .beta_managed_agents_session_deleted_event import BetaManagedAgentsSessionDeletedEvent from ..beta_managed_agents_session_updated_event import BetaManagedAgentsSessionUpdatedEvent from ..beta_managed_agents_user_tool_result_event import BetaManagedAgentsUserToolResultEvent from .beta_managed_agents_agent_tool_result_event import BetaManagedAgentsAgentToolResultEvent from .beta_managed_agents_agent_mcp_tool_use_event import BetaManagedAgentsAgentMCPToolUseEvent from .beta_managed_agents_session_status_idle_event import BetaManagedAgentsSessionStatusIdleEvent from .beta_managed_agents_user_define_outcome_event import BetaManagedAgentsUserDefineOutcomeEvent from .beta_managed_agents_agent_custom_tool_use_event import BetaManagedAgentsAgentCustomToolUseEvent from .beta_managed_agents_agent_mcp_tool_result_event import BetaManagedAgentsAgentMCPToolResultEvent from .beta_managed_agents_session_status_running_event import BetaManagedAgentsSessionStatusRunningEvent from .beta_managed_agents_session_thread_created_event import BetaManagedAgentsSessionThreadCreatedEvent from .beta_managed_agents_span_model_request_end_event import BetaManagedAgentsSpanModelRequestEndEvent from .beta_managed_agents_user_tool_confirmation_event import BetaManagedAgentsUserToolConfirmationEvent from .beta_managed_agents_user_custom_tool_result_event import BetaManagedAgentsUserCustomToolResultEvent from .beta_managed_agents_span_model_request_start_event import BetaManagedAgentsSpanModelRequestStartEvent from .beta_managed_agents_agent_thread_message_sent_event import BetaManagedAgentsAgentThreadMessageSentEvent from .beta_managed_agents_session_status_terminated_event import BetaManagedAgentsSessionStatusTerminatedEvent from .beta_managed_agents_session_status_rescheduled_event import BetaManagedAgentsSessionStatusRescheduledEvent from .beta_managed_agents_session_thread_status_idle_event import BetaManagedAgentsSessionThreadStatusIdleEvent from .beta_managed_agents_span_outcome_evaluation_end_event import BetaManagedAgentsSpanOutcomeEvaluationEndEvent from .beta_managed_agents_agent_thread_message_received_event import BetaManagedAgentsAgentThreadMessageReceivedEvent from .beta_managed_agents_session_thread_status_running_event import BetaManagedAgentsSessionThreadStatusRunningEvent from .beta_managed_agents_span_outcome_evaluation_start_event import BetaManagedAgentsSpanOutcomeEvaluationStartEvent from .beta_managed_agents_agent_thread_context_compacted_event import BetaManagedAgentsAgentThreadContextCompactedEvent from .beta_managed_agents_span_outcome_evaluation_ongoing_event import ( BetaManagedAgentsSpanOutcomeEvaluationOngoingEvent, ) from .beta_managed_agents_session_thread_status_terminated_event import ( BetaManagedAgentsSessionThreadStatusTerminatedEvent, ) from .beta_managed_agents_session_thread_status_rescheduled_event import ( BetaManagedAgentsSessionThreadStatusRescheduledEvent, ) __all__ = ["BetaManagedAgentsStreamSessionThreadEvents"] BetaManagedAgentsStreamSessionThreadEvents: TypeAlias = Annotated[ Union[ BetaManagedAgentsUserMessageEvent, BetaManagedAgentsUserInterruptEvent, BetaManagedAgentsUserToolConfirmationEvent, BetaManagedAgentsUserCustomToolResultEvent, BetaManagedAgentsAgentCustomToolUseEvent, BetaManagedAgentsAgentMessageEvent, BetaManagedAgentsAgentThinkingEvent, BetaManagedAgentsAgentMCPToolUseEvent, BetaManagedAgentsAgentMCPToolResultEvent, BetaManagedAgentsAgentToolUseEvent, BetaManagedAgentsAgentToolResultEvent, BetaManagedAgentsAgentThreadMessageReceivedEvent, BetaManagedAgentsAgentThreadMessageSentEvent, BetaManagedAgentsAgentThreadContextCompactedEvent, BetaManagedAgentsSessionErrorEvent, BetaManagedAgentsSessionStatusRescheduledEvent, BetaManagedAgentsSessionStatusRunningEvent, BetaManagedAgentsSessionStatusIdleEvent, BetaManagedAgentsSessionStatusTerminatedEvent, BetaManagedAgentsSessionThreadCreatedEvent, BetaManagedAgentsSpanOutcomeEvaluationStartEvent, BetaManagedAgentsSpanOutcomeEvaluationEndEvent, BetaManagedAgentsSpanModelRequestStartEvent, BetaManagedAgentsSpanModelRequestEndEvent, BetaManagedAgentsSpanOutcomeEvaluationOngoingEvent, BetaManagedAgentsUserDefineOutcomeEvent, BetaManagedAgentsSessionDeletedEvent, BetaManagedAgentsSessionThreadStatusRunningEvent, BetaManagedAgentsSessionThreadStatusIdleEvent, BetaManagedAgentsSessionThreadStatusTerminatedEvent, BetaManagedAgentsUserToolResultEvent, BetaManagedAgentsSessionThreadStatusRescheduledEvent, BetaManagedAgentsSessionUpdatedEvent, BetaManagedAgentsStartEvent, BetaManagedAgentsDeltaEvent, BetaManagedAgentsSystemMessageEvent, ], PropertyInfo(discriminator="type"), ] beta_managed_agents_system_message_event_params.py000066400000000000000000000020461523216435200365500ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Iterable from typing_extensions import Literal, Required, TypedDict from ..beta_managed_agents_system_content_block_param import BetaManagedAgentsSystemContentBlockParam __all__ = ["BetaManagedAgentsSystemMessageEventParams"] class BetaManagedAgentsSystemMessageEventParams(TypedDict, total=False): """ Privileged context for the accompanying turn and all subsequent turns, appended to the session's system context as a `role: "system"` turn rather than replacing the top-level system prompt. At most one per request: it must be the final event and immediately follow the `user.message`, `user.tool_result`, or `user.custom_tool_result` it accompanies. Only supported on models that accept mid-conversation system messages. """ content: Required[Iterable[BetaManagedAgentsSystemContentBlockParam]] """System content blocks to append. Text-only.""" type: Required[Literal["system.message"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions/beta_managed_agents_text_block.py000066400000000000000000000005371523216435200331740ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsTextBlock"] class BetaManagedAgentsTextBlock(BaseModel): """Regular text content.""" text: str """The text content.""" type: Literal["text"] beta_managed_agents_text_block_param.py000066400000000000000000000006401523216435200342700ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsTextBlockParam"] class BetaManagedAgentsTextBlockParam(TypedDict, total=False): """Regular text content.""" text: Required[str] """The text content.""" type: Required[Literal["text"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions/beta_managed_agents_text_rubric.py000066400000000000000000000006661523216435200333730ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsTextRubric"] class BetaManagedAgentsTextRubric(BaseModel): """Rubric content provided inline as text.""" content: str """Rubric content. Plain text or markdown — the grader treats it as freeform text.""" type: Literal["text"] beta_managed_agents_text_rubric_params.py000066400000000000000000000010421523216435200346440ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsTextRubricParams"] class BetaManagedAgentsTextRubricParams(TypedDict, total=False): """Rubric content provided inline as text.""" content: Required[str] """Rubric content. Plain text or markdown — the grader treats it as freeform text. Maximum 262144 characters. """ type: Required[Literal["text"]] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions/beta_managed_agents_unknown_error.py000066400000000000000000000023611523216435200337430ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Literal, Annotated, TypeAlias from ...._utils import PropertyInfo from ...._models import BaseModel from .beta_managed_agents_retry_status_retrying import BetaManagedAgentsRetryStatusRetrying from .beta_managed_agents_retry_status_terminal import BetaManagedAgentsRetryStatusTerminal from .beta_managed_agents_retry_status_exhausted import BetaManagedAgentsRetryStatusExhausted __all__ = ["BetaManagedAgentsUnknownError", "RetryStatus"] RetryStatus: TypeAlias = Annotated[ Union[ BetaManagedAgentsRetryStatusRetrying, BetaManagedAgentsRetryStatusExhausted, BetaManagedAgentsRetryStatusTerminal, ], PropertyInfo(discriminator="type"), ] class BetaManagedAgentsUnknownError(BaseModel): """An unknown or unexpected error occurred during session execution. A fallback variant; clients that don't recognize a new error code can match on `retry_status` and `message` alone. """ message: str """Human-readable error description.""" retry_status: RetryStatus """What the client should do next in response to this error.""" type: Literal["unknown_error"] beta_managed_agents_url_document_source.py000066400000000000000000000005771523216435200350430ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsURLDocumentSource"] class BetaManagedAgentsURLDocumentSource(BaseModel): """Document referenced by URL.""" type: Literal["url"] url: str """URL of the document to fetch.""" beta_managed_agents_url_document_source_param.py000066400000000000000000000007001523216435200362070ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsURLDocumentSourceParam"] class BetaManagedAgentsURLDocumentSourceParam(TypedDict, total=False): """Document referenced by URL.""" type: Required[Literal["url"]] url: Required[str] """URL of the document to fetch.""" beta_managed_agents_url_image_source.py000066400000000000000000000005631523216435200343020ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsURLImageSource"] class BetaManagedAgentsURLImageSource(BaseModel): """Image referenced by URL.""" type: Literal["url"] url: str """URL of the image to fetch.""" beta_managed_agents_url_image_source_param.py000066400000000000000000000006641523216435200354640ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsURLImageSourceParam"] class BetaManagedAgentsURLImageSourceParam(TypedDict, total=False): """Image referenced by URL.""" type: Required[Literal["url"]] url: Required[str] """URL of the image to fetch.""" beta_managed_agents_user_custom_tool_result_event.py000066400000000000000000000036601523216435200371630ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Union, Optional from datetime import datetime from typing_extensions import Literal, Annotated, TypeAlias from ...._utils import PropertyInfo from ...._models import BaseModel from .beta_managed_agents_text_block import BetaManagedAgentsTextBlock from .beta_managed_agents_image_block import BetaManagedAgentsImageBlock from .beta_managed_agents_document_block import BetaManagedAgentsDocumentBlock from .beta_managed_agents_search_result_block import BetaManagedAgentsSearchResultBlock __all__ = ["BetaManagedAgentsUserCustomToolResultEvent", "Content"] Content: TypeAlias = Annotated[ Union[ BetaManagedAgentsTextBlock, BetaManagedAgentsImageBlock, BetaManagedAgentsDocumentBlock, BetaManagedAgentsSearchResultBlock, ], PropertyInfo(discriminator="type"), ] class BetaManagedAgentsUserCustomToolResultEvent(BaseModel): """Event sent by the client providing the result of a custom tool execution.""" id: str """Unique identifier for this event.""" custom_tool_use_id: str """ The id of the `agent.custom_tool_use` event this result corresponds to, which can be found in the last `session.status_idle` [event's](https://platform.claude.com/docs/en/api/beta/sessions/events/list#beta_managed_agents_session_requires_action.event_ids) `stop_reason.event_ids` field. """ type: Literal["user.custom_tool_result"] content: Optional[List[Content]] = None """The result content returned by the tool.""" is_error: Optional[bool] = None """Whether the tool execution resulted in an error.""" processed_at: Optional[datetime] = None """A timestamp in RFC 3339 format""" session_thread_id: Optional[str] = None """Routes this result to a subagent thread. Copy from the `agent.custom_tool_use` event's `session_thread_id`. """ beta_managed_agents_user_custom_tool_result_event_params.py000066400000000000000000000031031523216435200405160ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .beta_managed_agents_text_block_param import BetaManagedAgentsTextBlockParam from .beta_managed_agents_image_block_param import BetaManagedAgentsImageBlockParam from .beta_managed_agents_document_block_param import BetaManagedAgentsDocumentBlockParam from .beta_managed_agents_search_result_block_param import BetaManagedAgentsSearchResultBlockParam __all__ = ["BetaManagedAgentsUserCustomToolResultEventParams", "Content"] Content: TypeAlias = Union[ BetaManagedAgentsTextBlockParam, BetaManagedAgentsImageBlockParam, BetaManagedAgentsDocumentBlockParam, BetaManagedAgentsSearchResultBlockParam, ] class BetaManagedAgentsUserCustomToolResultEventParams(TypedDict, total=False): """Parameters for providing the result of a custom tool execution.""" custom_tool_use_id: Required[str] """ The id of the `agent.custom_tool_use` event this result corresponds to, which can be found in the last `session.status_idle` [event's](https://platform.claude.com/docs/en/api/beta/sessions/events/list#beta_managed_agents_session_requires_action.event_ids) `stop_reason.event_ids` field. """ type: Required[Literal["user.custom_tool_result"]] content: Iterable[Content] """The result content returned by the tool.""" is_error: Optional[bool] """Whether the tool execution resulted in an error.""" beta_managed_agents_user_define_outcome_event.py000066400000000000000000000027341523216435200362040ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union, Optional from datetime import datetime from typing_extensions import Literal, Annotated, TypeAlias from ...._utils import PropertyInfo from ...._models import BaseModel from .beta_managed_agents_file_rubric import BetaManagedAgentsFileRubric from .beta_managed_agents_text_rubric import BetaManagedAgentsTextRubric __all__ = ["BetaManagedAgentsUserDefineOutcomeEvent", "Rubric"] Rubric: TypeAlias = Annotated[ Union[BetaManagedAgentsFileRubric, BetaManagedAgentsTextRubric], PropertyInfo(discriminator="type") ] class BetaManagedAgentsUserDefineOutcomeEvent(BaseModel): """Echo of a `user.define_outcome` input event. Carries the server-generated `outcome_id` that subsequent `span.outcome_evaluation_*` events reference. """ id: str """Unique identifier for this event.""" description: str """What the agent should produce. Copied from the input event.""" max_iterations: Optional[int] = None """Evaluate-then-revise cycles before giving up. Default 3, max 20.""" outcome_id: str """Server-generated `outc_` ID for this outcome. Referenced by `span.outcome_evaluation_*` events and the session's `outcome_evaluations` list. """ processed_at: datetime """A timestamp in RFC 3339 format""" rubric: Rubric """Rubric for grading the quality of an outcome.""" type: Literal["user.define_outcome"] beta_managed_agents_user_define_outcome_event_params.py000066400000000000000000000021331523216435200375400ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .beta_managed_agents_file_rubric_params import BetaManagedAgentsFileRubricParams from .beta_managed_agents_text_rubric_params import BetaManagedAgentsTextRubricParams __all__ = ["BetaManagedAgentsUserDefineOutcomeEventParams", "Rubric"] Rubric: TypeAlias = Union[BetaManagedAgentsFileRubricParams, BetaManagedAgentsTextRubricParams] class BetaManagedAgentsUserDefineOutcomeEventParams(TypedDict, total=False): """Parameters for defining an outcome the agent should work toward. The agent begins work on receipt. """ description: Required[str] """What the agent should produce. This is the task specification.""" rubric: Required[Rubric] """Rubric for grading the quality of an outcome.""" type: Required[Literal["user.define_outcome"]] max_iterations: Optional[int] """Eval→revision cycles before giving up. Default 3, max 20.""" beta_managed_agents_user_interrupt_event.py000066400000000000000000000015041523216435200352450ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from datetime import datetime from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsUserInterruptEvent"] class BetaManagedAgentsUserInterruptEvent(BaseModel): """An interrupt event that pauses agent execution and returns control to the user.""" id: str """Unique identifier for this event.""" type: Literal["user.interrupt"] processed_at: Optional[datetime] = None """A timestamp in RFC 3339 format""" session_thread_id: Optional[str] = None """ If absent, interrupts every non-archived thread in a multiagent session (or the primary alone in a single-agent session). If present, interrupts only the named thread. """ beta_managed_agents_user_interrupt_event_params.py000066400000000000000000000012611523216435200366100ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsUserInterruptEventParams"] class BetaManagedAgentsUserInterruptEventParams(TypedDict, total=False): """Parameters for sending an interrupt to pause the agent.""" type: Required[Literal["user.interrupt"]] session_thread_id: Optional[str] """ If absent, interrupts every non-archived thread in a multiagent session (or the primary alone in a single-agent session). If present, interrupts only the named thread. """ beta_managed_agents_user_message_event.py000066400000000000000000000021521523216435200346350ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Union, Optional from datetime import datetime from typing_extensions import Literal, Annotated, TypeAlias from ...._utils import PropertyInfo from ...._models import BaseModel from .beta_managed_agents_text_block import BetaManagedAgentsTextBlock from .beta_managed_agents_image_block import BetaManagedAgentsImageBlock from .beta_managed_agents_document_block import BetaManagedAgentsDocumentBlock __all__ = ["BetaManagedAgentsUserMessageEvent", "Content"] Content: TypeAlias = Annotated[ Union[BetaManagedAgentsTextBlock, BetaManagedAgentsImageBlock, BetaManagedAgentsDocumentBlock], PropertyInfo(discriminator="type"), ] class BetaManagedAgentsUserMessageEvent(BaseModel): """A user message event in the session conversation.""" id: str """Unique identifier for this event.""" content: List[Content] """Array of content blocks comprising the user message.""" type: Literal["user.message"] processed_at: Optional[datetime] = None """A timestamp in RFC 3339 format""" beta_managed_agents_user_message_event_params.py000066400000000000000000000017111523216435200362000ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Iterable from typing_extensions import Literal, Required, TypeAlias, TypedDict from .beta_managed_agents_text_block_param import BetaManagedAgentsTextBlockParam from .beta_managed_agents_image_block_param import BetaManagedAgentsImageBlockParam from .beta_managed_agents_document_block_param import BetaManagedAgentsDocumentBlockParam __all__ = ["BetaManagedAgentsUserMessageEventParams", "Content"] Content: TypeAlias = Union[ BetaManagedAgentsTextBlockParam, BetaManagedAgentsImageBlockParam, BetaManagedAgentsDocumentBlockParam ] class BetaManagedAgentsUserMessageEventParams(TypedDict, total=False): """Parameters for sending a user message to the session.""" content: Required[Iterable[Content]] """Array of content blocks for the user message.""" type: Required[Literal["user.message"]] beta_managed_agents_user_tool_confirmation_event.py000066400000000000000000000027141523216435200367420ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from datetime import datetime from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsUserToolConfirmationEvent"] class BetaManagedAgentsUserToolConfirmationEvent(BaseModel): """A tool confirmation event that approves or denies a pending tool execution.""" id: str """Unique identifier for this event.""" result: Literal["allow", "deny"] """UserToolConfirmationResult enum""" tool_use_id: str """ The id of the `agent.tool_use` or `agent.mcp_tool_use` event this result corresponds to, which can be found in the last `session.status_idle` [event's](https://platform.claude.com/docs/en/api/beta/sessions/events/list#beta_managed_agents_session_requires_action.event_ids) `stop_reason.event_ids` field. """ type: Literal["user.tool_confirmation"] deny_message: Optional[str] = None """Optional message providing context for a 'deny' decision. Only allowed when result is 'deny'. """ processed_at: Optional[datetime] = None """A timestamp in RFC 3339 format""" session_thread_id: Optional[str] = None """ When set, the confirmation routes to this subagent's thread rather than the primary. Echo this from the `session_thread_id` on the `agent.tool_use` or `agent.mcp_tool_use` event that prompted the approval. """ beta_managed_agents_user_tool_confirmation_event_params.py000066400000000000000000000021011523216435200402730ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsUserToolConfirmationEventParams"] class BetaManagedAgentsUserToolConfirmationEventParams(TypedDict, total=False): """Parameters for confirming or denying a tool execution request.""" result: Required[Literal["allow", "deny"]] """UserToolConfirmationResult enum""" tool_use_id: Required[str] """ The id of the `agent.tool_use` or `agent.mcp_tool_use` event this result corresponds to, which can be found in the last `session.status_idle` [event's](https://platform.claude.com/docs/en/api/beta/sessions/events/list#beta_managed_agents_session_requires_action.event_ids) `stop_reason.event_ids` field. """ type: Required[Literal["user.tool_confirmation"]] deny_message: Optional[str] """Optional message providing context for a 'deny' decision. Only allowed when result is 'deny'. """ beta_managed_agents_user_tool_result_event_params.py000066400000000000000000000032541523216435200371330ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .beta_managed_agents_text_block_param import BetaManagedAgentsTextBlockParam from .beta_managed_agents_image_block_param import BetaManagedAgentsImageBlockParam from .beta_managed_agents_document_block_param import BetaManagedAgentsDocumentBlockParam from .beta_managed_agents_search_result_block_param import BetaManagedAgentsSearchResultBlockParam __all__ = ["BetaManagedAgentsUserToolResultEventParams", "Content"] Content: TypeAlias = Union[ BetaManagedAgentsTextBlockParam, BetaManagedAgentsImageBlockParam, BetaManagedAgentsDocumentBlockParam, BetaManagedAgentsSearchResultBlockParam, ] class BetaManagedAgentsUserToolResultEventParams(TypedDict, total=False): """Parameters for providing the result of an agent-toolset tool execution. Only valid on `self_hosted` environments, where sandbox-routed tools are executed by the client rather than the server. """ tool_use_id: Required[str] """ The id of the `agent.tool_use` event this result corresponds to, which can be found in the last `session.status_idle` [event's](https://platform.claude.com/docs/en/api/beta/sessions/events/list#beta_managed_agents_session_requires_action.event_ids) `stop_reason.event_ids` field. """ type: Required[Literal["user.tool_result"]] content: Iterable[Content] """The result content returned by the tool.""" is_error: Optional[bool] """Whether the tool execution resulted in an error.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions/event_list_params.py000066400000000000000000000040101523216435200305330ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Union from datetime import datetime from typing_extensions import Literal, Annotated, TypedDict from ...._types import SequenceNotStr from ...._utils import PropertyInfo from ...anthropic_beta_param import AnthropicBetaParam __all__ = ["EventListParams"] class EventListParams(TypedDict, total=False): created_at_gt: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[gt]", format="iso8601")] """Return events created after this time (exclusive). Compared against the event's `processed_at` value. """ created_at_gte: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[gte]", format="iso8601")] """Return events created at or after this time (inclusive). Compared against the event's `processed_at` value. """ created_at_lt: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[lt]", format="iso8601")] """Return events created before this time (exclusive). Compared against the event's `processed_at` value. """ created_at_lte: Annotated[Union[str, datetime], PropertyInfo(alias="created_at[lte]", format="iso8601")] """Return events created at or before this time (inclusive). Compared against the event's `processed_at` value. """ limit: int """Query parameter for limit""" order: Literal["asc", "desc"] """Sort direction for results, ordered by the event's `processed_at`. Defaults to asc (chronological). """ page: str """Opaque pagination cursor from a previous response's next_page.""" types: SequenceNotStr[str] """Filter by event type. Values match the `type` field on returned events (for example, `user.message` or `agent.tool_use`). Omit to return all event types. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions/event_send_params.py000066400000000000000000000013311523216435200305140ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Iterable from typing_extensions import Required, Annotated, TypedDict from ...._utils import PropertyInfo from ...anthropic_beta_param import AnthropicBetaParam from .beta_managed_agents_event_params import BetaManagedAgentsEventParams __all__ = ["EventSendParams"] class EventSendParams(TypedDict, total=False): events: Required[Iterable[BetaManagedAgentsEventParams]] """Events to send to the `session`.""" betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions/event_stream_params.py000066400000000000000000000025741523216435200310700ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List from typing_extensions import Annotated, TypedDict from ...._utils import PropertyInfo from ...anthropic_beta_param import AnthropicBetaParam from ..beta_managed_agents_delta_type import BetaManagedAgentsDeltaType __all__ = ["EventStreamParams"] class EventStreamParams(TypedDict, total=False): event_deltas: List[BetaManagedAgentsDeltaType] """ When set, this connection also receives streaming deltas (`event_start`, `event_delta`) while an event is being produced, before the event itself arrives. Deltas are best-effort; when the final event is produced it carries the complete content. A model request that ends early (an error or interrupt) produces no final event — its terminal `span.model_request_end` closes the preview. Accepts one or more event types to preview and may be repeated: `agent.message` streams `content_delta` fragments; `agent.thinking` is start-only — a signal that the agent has begun extended thinking, concluded by the `agent.thinking` event itself. Only previews of the requested event types are sent. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions/resource_add_params.py000066400000000000000000000014231523216435200310230ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Literal, Required, Annotated, TypedDict from ...._utils import PropertyInfo from ...anthropic_beta_param import AnthropicBetaParam __all__ = ["ResourceAddParams"] class ResourceAddParams(TypedDict, total=False): file_id: Required[str] """ID of a previously uploaded file.""" type: Required[Literal["file"]] mount_path: Optional[str] """Mount path in the container. Defaults to `/mnt/session/uploads/`.""" betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions/resource_list_params.py000066400000000000000000000013541523216435200312510ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List from typing_extensions import Annotated, TypedDict from ...._utils import PropertyInfo from ...anthropic_beta_param import AnthropicBetaParam __all__ = ["ResourceListParams"] class ResourceListParams(TypedDict, total=False): limit: int """Maximum number of resources to return per page (max 1000). If omitted, returns all resources. """ page: str """Opaque cursor from a previous response's next_page field.""" betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions/resource_retrieve_response.py000066400000000000000000000013441523216435200324750ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from ...._utils import PropertyInfo from .beta_managed_agents_file_resource import BetaManagedAgentsFileResource from .beta_managed_agents_memory_store_resource import BetaManagedAgentsMemoryStoreResource from .beta_managed_agents_github_repository_resource import BetaManagedAgentsGitHubRepositoryResource __all__ = ["ResourceRetrieveResponse"] ResourceRetrieveResponse: TypeAlias = Annotated[ Union[ BetaManagedAgentsGitHubRepositoryResource, BetaManagedAgentsFileResource, BetaManagedAgentsMemoryStoreResource ], PropertyInfo(discriminator="type"), ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions/resource_update_params.py000066400000000000000000000013571523216435200315630ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List from typing_extensions import Required, Annotated, TypedDict from ...._utils import PropertyInfo from ...anthropic_beta_param import AnthropicBetaParam __all__ = ["ResourceUpdateParams"] class ResourceUpdateParams(TypedDict, total=False): session_id: Required[str] authorization_token: Required[str] """New authorization token for the resource. Currently only `github_repository` resources support token rotation. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions/resource_update_response.py000066400000000000000000000013401523216435200321260ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from ...._utils import PropertyInfo from .beta_managed_agents_file_resource import BetaManagedAgentsFileResource from .beta_managed_agents_memory_store_resource import BetaManagedAgentsMemoryStoreResource from .beta_managed_agents_github_repository_resource import BetaManagedAgentsGitHubRepositoryResource __all__ = ["ResourceUpdateResponse"] ResourceUpdateResponse: TypeAlias = Annotated[ Union[ BetaManagedAgentsGitHubRepositoryResource, BetaManagedAgentsFileResource, BetaManagedAgentsMemoryStoreResource ], PropertyInfo(discriminator="type"), ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions/thread_list_params.py000066400000000000000000000012771523216435200306750ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List from typing_extensions import Annotated, TypedDict from ...._utils import PropertyInfo from ...anthropic_beta_param import AnthropicBetaParam __all__ = ["ThreadListParams"] class ThreadListParams(TypedDict, total=False): limit: int """Maximum results per page. Defaults to 1000.""" page: str """Opaque pagination cursor from a previous response's next_page. Forward-only.""" betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions/threads/000077500000000000000000000000001523216435200261015ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions/threads/__init__.py000066400000000000000000000004051523216435200302110ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from .event_list_params import EventListParams as EventListParams from .event_stream_params import EventStreamParams as EventStreamParams anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions/threads/event_list_params.py000066400000000000000000000012421523216435200321710ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List from typing_extensions import Required, Annotated, TypedDict from ....._utils import PropertyInfo from ....anthropic_beta_param import AnthropicBetaParam __all__ = ["EventListParams"] class EventListParams(TypedDict, total=False): session_id: Required[str] limit: int """Query parameter for limit""" page: str """Query parameter for page""" betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/sessions/threads/event_stream_params.py000066400000000000000000000026501523216435200325150ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List from typing_extensions import Required, Annotated, TypedDict from ....._utils import PropertyInfo from ....anthropic_beta_param import AnthropicBetaParam from ...beta_managed_agents_delta_type import BetaManagedAgentsDeltaType __all__ = ["EventStreamParams"] class EventStreamParams(TypedDict, total=False): session_id: Required[str] event_deltas: List[BetaManagedAgentsDeltaType] """ When set, this connection also receives streaming deltas (`event_start`, `event_delta`) while an event is being produced, before the event itself arrives. Deltas are best-effort; when the final event is produced it carries the complete content. A model request that ends early (an error or interrupt) produces no final event — its terminal `span.model_request_end` closes the preview. Accepts one or more event types to preview and may be repeated: `agent.message` streams `content_delta` fragments; `agent.thinking` is start-only — a signal that the agent has begun extended thinking, concluded by the `agent.thinking` event itself. Only previews of the requested event types are sent. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/skill_create_params.py000066400000000000000000000017341523216435200271640ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Required, Annotated, TypedDict from ..._types import FileTypes, SequenceNotStr from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam __all__ = ["SkillCreateParams"] class SkillCreateParams(TypedDict, total=False): files: Required[SequenceNotStr[FileTypes]] """Files to upload for the skill. All files must be in the same top-level directory and must include a SKILL.md file at the root of that directory. """ display_title: Optional[str] """Display title for the skill. This is a human-readable label that is not included in the prompt sent to the model. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/skill_create_response.py000066400000000000000000000022111523216435200275260ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from ..._models import BaseModel __all__ = ["SkillCreateResponse"] class SkillCreateResponse(BaseModel): id: str """Unique identifier for the skill. The format and length of IDs may change over time. """ created_at: str """ISO 8601 timestamp of when the skill was created.""" display_title: Optional[str] = None """Display title for the skill. This is a human-readable label that is not included in the prompt sent to the model. """ latest_version: Optional[str] = None """The latest version identifier for the skill. This represents the most recent version of the skill that has been created. """ source: str """Source of the skill. This may be one of the following values: - `"custom"`: the skill was created by a user - `"anthropic"`: the skill was created by Anthropic """ type: str """Object type. For Skills, this is always `"skill"`. """ updated_at: str """ISO 8601 timestamp of when the skill was last updated.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/skill_delete_response.py000066400000000000000000000006351523216435200275350ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from ..._models import BaseModel __all__ = ["SkillDeleteResponse"] class SkillDeleteResponse(BaseModel): id: str """Unique identifier for the skill. The format and length of IDs may change over time. """ type: str """Deleted object type. For Skills, this is always `"skill_deleted"`. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/skill_list_params.py000066400000000000000000000021131523216435200266640ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Annotated, TypedDict from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam __all__ = ["SkillListParams"] class SkillListParams(TypedDict, total=False): limit: int """Number of results to return per page. Maximum value is 100. Defaults to 20. """ page: Optional[str] """Pagination token for fetching a specific page of results. Pass the value from a previous response's `next_page` field to get the next page of results. """ source: Optional[str] """Filter skills by source. If provided, only skills from the specified source will be returned: - `"custom"`: only return user-created skills - `"anthropic"`: only return Anthropic-created skills """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/skill_list_response.py000066400000000000000000000022051523216435200272410ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from ..._models import BaseModel __all__ = ["SkillListResponse"] class SkillListResponse(BaseModel): id: str """Unique identifier for the skill. The format and length of IDs may change over time. """ created_at: str """ISO 8601 timestamp of when the skill was created.""" display_title: Optional[str] = None """Display title for the skill. This is a human-readable label that is not included in the prompt sent to the model. """ latest_version: Optional[str] = None """The latest version identifier for the skill. This represents the most recent version of the skill that has been created. """ source: str """Source of the skill. This may be one of the following values: - `"custom"`: the skill was created by a user - `"anthropic"`: the skill was created by Anthropic """ type: str """Object type. For Skills, this is always `"skill"`. """ updated_at: str """ISO 8601 timestamp of when the skill was last updated.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/skill_retrieve_response.py000066400000000000000000000022151523216435200301140ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from ..._models import BaseModel __all__ = ["SkillRetrieveResponse"] class SkillRetrieveResponse(BaseModel): id: str """Unique identifier for the skill. The format and length of IDs may change over time. """ created_at: str """ISO 8601 timestamp of when the skill was created.""" display_title: Optional[str] = None """Display title for the skill. This is a human-readable label that is not included in the prompt sent to the model. """ latest_version: Optional[str] = None """The latest version identifier for the skill. This represents the most recent version of the skill that has been created. """ source: str """Source of the skill. This may be one of the following values: - `"custom"`: the skill was created by a user - `"anthropic"`: the skill was created by Anthropic """ type: str """Object type. For Skills, this is always `"skill"`. """ updated_at: str """ISO 8601 timestamp of when the skill was last updated.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/skills/000077500000000000000000000000001523216435200241025ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/skills/__init__.py000066400000000000000000000011411523216435200262100ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from .version_list_params import VersionListParams as VersionListParams from .version_create_params import VersionCreateParams as VersionCreateParams from .version_list_response import VersionListResponse as VersionListResponse from .version_create_response import VersionCreateResponse as VersionCreateResponse from .version_delete_response import VersionDeleteResponse as VersionDeleteResponse from .version_retrieve_response import VersionRetrieveResponse as VersionRetrieveResponse anthropic-sdk-python-0.120.2/src/anthropic/types/beta/skills/version_create_params.py000066400000000000000000000014551523216435200310340ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List from typing_extensions import Required, Annotated, TypedDict from ...._types import FileTypes, SequenceNotStr from ...._utils import PropertyInfo from ...anthropic_beta_param import AnthropicBetaParam __all__ = ["VersionCreateParams"] class VersionCreateParams(TypedDict, total=False): files: Required[SequenceNotStr[FileTypes]] """Files to upload for the skill. All files must be in the same top-level directory and must include a SKILL.md file at the root of that directory. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/skills/version_create_response.py000066400000000000000000000022431523216435200314030ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from ...._models import BaseModel __all__ = ["VersionCreateResponse"] class VersionCreateResponse(BaseModel): id: str """Unique identifier for the skill version. The format and length of IDs may change over time. """ created_at: str """ISO 8601 timestamp of when the skill version was created.""" description: str """Description of the skill version. This is extracted from the SKILL.md file in the skill upload. """ directory: str """Directory name of the skill version. This is the top-level directory name that was extracted from the uploaded files. """ name: str """Human-readable name of the skill version. This is extracted from the SKILL.md file in the skill upload. """ skill_id: str """Identifier for the skill that this version belongs to.""" type: str """Object type. For Skill Versions, this is always `"skill_version"`. """ version: str """Version identifier for the skill. Each version is identified by a Unix epoch timestamp (e.g., "1759178010641129"). """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/skills/version_delete_response.py000066400000000000000000000007211523216435200314010ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from ...._models import BaseModel __all__ = ["VersionDeleteResponse"] class VersionDeleteResponse(BaseModel): id: str """Version identifier for the skill. Each version is identified by a Unix epoch timestamp (e.g., "1759178010641129"). """ type: str """Deleted object type. For Skill Versions, this is always `"skill_version_deleted"`. """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/skills/version_list_params.py000066400000000000000000000014051523216435200305370ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Annotated, TypedDict from ...._utils import PropertyInfo from ...anthropic_beta_param import AnthropicBetaParam __all__ = ["VersionListParams"] class VersionListParams(TypedDict, total=False): limit: Optional[int] """Number of items to return per page. Defaults to `20`. Ranges from `1` to `1000`. """ page: Optional[str] """Optionally set to the `next_page` token from the previous response.""" betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/skills/version_list_response.py000066400000000000000000000022371523216435200311160ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from ...._models import BaseModel __all__ = ["VersionListResponse"] class VersionListResponse(BaseModel): id: str """Unique identifier for the skill version. The format and length of IDs may change over time. """ created_at: str """ISO 8601 timestamp of when the skill version was created.""" description: str """Description of the skill version. This is extracted from the SKILL.md file in the skill upload. """ directory: str """Directory name of the skill version. This is the top-level directory name that was extracted from the uploaded files. """ name: str """Human-readable name of the skill version. This is extracted from the SKILL.md file in the skill upload. """ skill_id: str """Identifier for the skill that this version belongs to.""" type: str """Object type. For Skill Versions, this is always `"skill_version"`. """ version: str """Version identifier for the skill. Each version is identified by a Unix epoch timestamp (e.g., "1759178010641129"). """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/skills/version_retrieve_response.py000066400000000000000000000022471523216435200317710ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from ...._models import BaseModel __all__ = ["VersionRetrieveResponse"] class VersionRetrieveResponse(BaseModel): id: str """Unique identifier for the skill version. The format and length of IDs may change over time. """ created_at: str """ISO 8601 timestamp of when the skill version was created.""" description: str """Description of the skill version. This is extracted from the SKILL.md file in the skill upload. """ directory: str """Directory name of the skill version. This is the top-level directory name that was extracted from the uploaded files. """ name: str """Human-readable name of the skill version. This is extracted from the SKILL.md file in the skill upload. """ skill_id: str """Identifier for the skill that this version belongs to.""" type: str """Object type. For Skill Versions, this is always `"skill_version"`. """ version: str """Version identifier for the skill. Each version is identified by a Unix epoch timestamp (e.g., "1759178010641129"). """ anthropic-sdk-python-0.120.2/src/anthropic/types/beta/tunnel_create_params.py000066400000000000000000000012121523216435200273420ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Annotated, TypedDict from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam __all__ = ["TunnelCreateParams"] class TunnelCreateParams(TypedDict, total=False): display_name: Optional[str] """Optional human-readable name for the tunnel (1-255 characters).""" betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/tunnel_list_params.py000066400000000000000000000014771523216435200270670ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List from typing_extensions import Annotated, TypedDict from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam __all__ = ["TunnelListParams"] class TunnelListParams(TypedDict, total=False): include_archived: bool """Whether to include archived tunnels in the results. Defaults to false.""" limit: int """Maximum number of tunnels to return per page. Defaults to 20, maximum 1000.""" page: str """Opaque pagination cursor from a previous `list_tunnels` response.""" betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/tunnel_rotate_token_params.py000066400000000000000000000012161523216435200306010ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Annotated, TypedDict from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam __all__ = ["TunnelRotateTokenParams"] class TunnelRotateTokenParams(TypedDict, total=False): reason: Optional[str] """Optional free-text reason for the rotation, recorded for audit.""" betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/tunnels/000077500000000000000000000000001523216435200242715ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/tunnels/__init__.py000066400000000000000000000005751523216435200264110ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from .beta_tunnel_certificate import BetaTunnelCertificate as BetaTunnelCertificate from .certificate_list_params import CertificateListParams as CertificateListParams from .certificate_create_params import CertificateCreateParams as CertificateCreateParams anthropic-sdk-python-0.120.2/src/anthropic/types/beta/tunnels/beta_tunnel_certificate.py000066400000000000000000000015771523216435200315170ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from datetime import datetime from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaTunnelCertificate"] class BetaTunnelCertificate(BaseModel): """A CA certificate attached to a tunnel.""" id: str """Unique identifier for the certificate, prefixed with `tcrt_`.""" archived_at: Optional[datetime] = None """A timestamp in RFC 3339 format""" created_at: datetime """A timestamp in RFC 3339 format""" expires_at: Optional[datetime] = None """A timestamp in RFC 3339 format""" fingerprint: str """Lowercase hex SHA-256 fingerprint of the certificate's DER encoding.""" tunnel_id: str """ID of the tunnel the certificate is registered against.""" type: Literal["tunnel_certificate"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/tunnels/certificate_create_params.py000066400000000000000000000013271523216435200320160ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List from typing_extensions import Required, Annotated, TypedDict from ...._utils import PropertyInfo from ...anthropic_beta_param import AnthropicBetaParam __all__ = ["CertificateCreateParams"] class CertificateCreateParams(TypedDict, total=False): ca_certificate_pem: Required[str] """PEM-encoded X.509 CA certificate. Must contain exactly one certificate and no private-key material. Maximum 8KB. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/tunnels/certificate_list_params.py000066400000000000000000000015531523216435200315270ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List from typing_extensions import Annotated, TypedDict from ...._utils import PropertyInfo from ...anthropic_beta_param import AnthropicBetaParam __all__ = ["CertificateListParams"] class CertificateListParams(TypedDict, total=False): include_archived: bool """Whether to include archived certificates in the results. Defaults to false.""" limit: int """Maximum number of certificates to return per page. Defaults to 20, maximum 1000. """ page: str """Opaque pagination cursor from a previous `list_tunnel_certificates` response.""" betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/unwrap_webhook_event.py000066400000000000000000000011071523216435200274050ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from datetime import datetime from typing_extensions import Literal from ..._models import BaseModel from .beta_webhook_event_data import BetaWebhookEventData __all__ = ["UnwrapWebhookEvent"] class UnwrapWebhookEvent(BaseModel): id: str """Unique event identifier for idempotency.""" created_at: datetime """RFC 3339 timestamp when the event occurred.""" data: BetaWebhookEventData type: Literal["event"] """Object type. Always `event` for webhook payloads.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/user_profile_create_params.py000066400000000000000000000026751523216435200305510ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Optional from typing_extensions import Literal, Annotated, TypedDict from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam __all__ = ["UserProfileCreateParams"] class UserProfileCreateParams(TypedDict, total=False): external_id: Optional[str] """Platform's own identifier for this user. Not enforced unique. Maximum 255 characters. """ metadata: Dict[str, str] """Free-form key-value data to attach to this user profile. Maximum 16 keys, with keys up to 64 characters and values up to 512 characters. Values must be non-empty strings. """ name: Optional[str] """Display name of the entity this profile represents. Required when relationship is `resold` (the resold-to company's name); optional otherwise. Maximum 255 characters. """ relationship: Literal["external", "resold", "internal"] """ How the entity behind a user profile relates to the platform that owns the API key. `external`: an individual end-user of the platform. `resold`: a company the platform resells Claude access to. `internal`: the platform's own usage. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/user_profile_list_params.py000066400000000000000000000013211523216435200302440ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List from typing_extensions import Literal, Annotated, TypedDict from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam __all__ = ["UserProfileListParams"] class UserProfileListParams(TypedDict, total=False): limit: int """Query parameter for limit""" order: Literal["asc", "desc"] """Query parameter for order""" page: str """Query parameter for page""" betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/user_profile_update_params.py000066400000000000000000000027311523216435200305610ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Optional from typing_extensions import Literal, Annotated, TypedDict from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam __all__ = ["UserProfileUpdateParams"] class UserProfileUpdateParams(TypedDict, total=False): external_id: Optional[str] """If present, replaces the stored external_id. Omit to leave unchanged. Maximum 255 characters. """ metadata: Dict[str, str] """Key-value pairs to merge into the stored metadata. Keys provided overwrite existing values. To remove a key, set its value to an empty string. Keys not provided are left unchanged. Maximum 16 keys, with keys up to 64 characters and values up to 512 characters. """ name: Optional[str] """If present, replaces the stored name. Omit to leave unchanged. Maximum 255 characters. """ relationship: Optional[Literal["external", "resold", "internal"]] """ How the entity behind a user profile relates to the platform that owns the API key. `external`: an individual end-user of the platform. `resold`: a company the platform resells Claude access to. `internal`: the platform's own usage. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vault_create_params.py000066400000000000000000000014511523216435200271750ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List from typing_extensions import Required, Annotated, TypedDict from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam __all__ = ["VaultCreateParams"] class VaultCreateParams(TypedDict, total=False): display_name: Required[str] """Human-readable name for the vault. 1-255 characters.""" metadata: Dict[str, str] """Arbitrary key-value metadata to attach to the vault. Maximum 16 pairs, keys up to 64 chars, values up to 512 chars. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vault_list_params.py000066400000000000000000000014451523216435200267100ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List from typing_extensions import Annotated, TypedDict from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam __all__ = ["VaultListParams"] class VaultListParams(TypedDict, total=False): include_archived: bool """Whether to include archived vaults in the results.""" limit: int """Maximum number of vaults to return per page. Defaults to 20, maximum 100.""" page: str """Opaque pagination token from a previous `list_vaults` response.""" betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vault_update_params.py000066400000000000000000000014761523216435200272230ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Optional from typing_extensions import Annotated, TypedDict from ..._utils import PropertyInfo from ..anthropic_beta_param import AnthropicBetaParam __all__ = ["VaultUpdateParams"] class VaultUpdateParams(TypedDict, total=False): display_name: Optional[str] """Updated human-readable name for the vault. 1-255 characters.""" metadata: Optional[Dict[str, Optional[str]]] """Metadata patch. Set a key to a string to upsert it, or to null to delete it. Omitted keys are preserved. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults/000077500000000000000000000000001523216435200241175ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults/__init__.py000066400000000000000000000135471523216435200262420ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from .credential_list_params import CredentialListParams as CredentialListParams from .credential_create_params import CredentialCreateParams as CredentialCreateParams from .credential_update_params import CredentialUpdateParams as CredentialUpdateParams from .beta_managed_agents_mcp_probe import BetaManagedAgentsMCPProbe as BetaManagedAgentsMCPProbe from .beta_managed_agents_credential import BetaManagedAgentsCredential as BetaManagedAgentsCredential from .beta_managed_agents_refresh_object import BetaManagedAgentsRefreshObject as BetaManagedAgentsRefreshObject from .beta_managed_agents_deleted_credential import ( BetaManagedAgentsDeletedCredential as BetaManagedAgentsDeletedCredential, ) from .beta_managed_agents_credential_validation import ( BetaManagedAgentsCredentialValidation as BetaManagedAgentsCredentialValidation, ) from .beta_managed_agents_refresh_http_response import ( BetaManagedAgentsRefreshHTTPResponse as BetaManagedAgentsRefreshHTTPResponse, ) from .beta_managed_agents_mcp_oauth_auth_response import ( BetaManagedAgentsMCPOAuthAuthResponse as BetaManagedAgentsMCPOAuthAuthResponse, ) from .beta_managed_agents_mcp_oauth_create_params import ( BetaManagedAgentsMCPOAuthCreateParams as BetaManagedAgentsMCPOAuthCreateParams, ) from .beta_managed_agents_mcp_oauth_update_params import ( BetaManagedAgentsMCPOAuthUpdateParams as BetaManagedAgentsMCPOAuthUpdateParams, ) from .beta_managed_agents_mcp_oauth_refresh_params import ( BetaManagedAgentsMCPOAuthRefreshParams as BetaManagedAgentsMCPOAuthRefreshParams, ) from .beta_managed_agents_injection_location_params import ( BetaManagedAgentsInjectionLocationParams as BetaManagedAgentsInjectionLocationParams, ) from .beta_managed_agents_mcp_oauth_refresh_response import ( BetaManagedAgentsMCPOAuthRefreshResponse as BetaManagedAgentsMCPOAuthRefreshResponse, ) from .beta_managed_agents_injection_location_response import ( BetaManagedAgentsInjectionLocationResponse as BetaManagedAgentsInjectionLocationResponse, ) from .beta_managed_agents_static_bearer_auth_response import ( BetaManagedAgentsStaticBearerAuthResponse as BetaManagedAgentsStaticBearerAuthResponse, ) from .beta_managed_agents_static_bearer_create_params import ( BetaManagedAgentsStaticBearerCreateParams as BetaManagedAgentsStaticBearerCreateParams, ) from .beta_managed_agents_static_bearer_update_params import ( BetaManagedAgentsStaticBearerUpdateParams as BetaManagedAgentsStaticBearerUpdateParams, ) from .beta_managed_agents_credential_networking_params import ( BetaManagedAgentsCredentialNetworkingParams as BetaManagedAgentsCredentialNetworkingParams, ) from .beta_managed_agents_credential_validation_status import ( BetaManagedAgentsCredentialValidationStatus as BetaManagedAgentsCredentialValidationStatus, ) from .beta_managed_agents_token_endpoint_auth_none_param import ( BetaManagedAgentsTokenEndpointAuthNoneParam as BetaManagedAgentsTokenEndpointAuthNoneParam, ) from .beta_managed_agents_token_endpoint_auth_post_param import ( BetaManagedAgentsTokenEndpointAuthPostParam as BetaManagedAgentsTokenEndpointAuthPostParam, ) from .beta_managed_agents_mcp_oauth_refresh_update_params import ( BetaManagedAgentsMCPOAuthRefreshUpdateParams as BetaManagedAgentsMCPOAuthRefreshUpdateParams, ) from .beta_managed_agents_token_endpoint_auth_basic_param import ( BetaManagedAgentsTokenEndpointAuthBasicParam as BetaManagedAgentsTokenEndpointAuthBasicParam, ) from .beta_managed_agents_injection_location_update_params import ( BetaManagedAgentsInjectionLocationUpdateParams as BetaManagedAgentsInjectionLocationUpdateParams, ) from .beta_managed_agents_token_endpoint_auth_none_response import ( BetaManagedAgentsTokenEndpointAuthNoneResponse as BetaManagedAgentsTokenEndpointAuthNoneResponse, ) from .beta_managed_agents_token_endpoint_auth_post_response import ( BetaManagedAgentsTokenEndpointAuthPostResponse as BetaManagedAgentsTokenEndpointAuthPostResponse, ) from .beta_managed_agents_environment_variable_auth_response import ( BetaManagedAgentsEnvironmentVariableAuthResponse as BetaManagedAgentsEnvironmentVariableAuthResponse, ) from .beta_managed_agents_environment_variable_create_params import ( BetaManagedAgentsEnvironmentVariableCreateParams as BetaManagedAgentsEnvironmentVariableCreateParams, ) from .beta_managed_agents_environment_variable_update_params import ( BetaManagedAgentsEnvironmentVariableUpdateParams as BetaManagedAgentsEnvironmentVariableUpdateParams, ) from .beta_managed_agents_token_endpoint_auth_basic_response import ( BetaManagedAgentsTokenEndpointAuthBasicResponse as BetaManagedAgentsTokenEndpointAuthBasicResponse, ) from .beta_managed_agents_limited_credential_networking_params import ( BetaManagedAgentsLimitedCredentialNetworkingParams as BetaManagedAgentsLimitedCredentialNetworkingParams, ) from .beta_managed_agents_token_endpoint_auth_post_update_param import ( BetaManagedAgentsTokenEndpointAuthPostUpdateParam as BetaManagedAgentsTokenEndpointAuthPostUpdateParam, ) from .beta_managed_agents_limited_credential_networking_response import ( BetaManagedAgentsLimitedCredentialNetworkingResponse as BetaManagedAgentsLimitedCredentialNetworkingResponse, ) from .beta_managed_agents_token_endpoint_auth_basic_update_param import ( BetaManagedAgentsTokenEndpointAuthBasicUpdateParam as BetaManagedAgentsTokenEndpointAuthBasicUpdateParam, ) from .beta_managed_agents_unrestricted_credential_networking_params import ( BetaManagedAgentsUnrestrictedCredentialNetworkingParams as BetaManagedAgentsUnrestrictedCredentialNetworkingParams, ) from .beta_managed_agents_unrestricted_credential_networking_response import ( BetaManagedAgentsUnrestrictedCredentialNetworkingResponse as BetaManagedAgentsUnrestrictedCredentialNetworkingResponse, ) anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults/beta_managed_agents_credential.py000066400000000000000000000032511523216435200326140ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Dict, Union, Optional from datetime import datetime from typing_extensions import Literal, Annotated, TypeAlias from ...._utils import PropertyInfo from ...._models import BaseModel from .beta_managed_agents_mcp_oauth_auth_response import BetaManagedAgentsMCPOAuthAuthResponse from .beta_managed_agents_static_bearer_auth_response import BetaManagedAgentsStaticBearerAuthResponse from .beta_managed_agents_environment_variable_auth_response import BetaManagedAgentsEnvironmentVariableAuthResponse __all__ = ["BetaManagedAgentsCredential", "Auth"] Auth: TypeAlias = Annotated[ Union[ BetaManagedAgentsMCPOAuthAuthResponse, BetaManagedAgentsStaticBearerAuthResponse, BetaManagedAgentsEnvironmentVariableAuthResponse, ], PropertyInfo(discriminator="type"), ] class BetaManagedAgentsCredential(BaseModel): """A credential stored in a vault. Sensitive fields are never returned in responses. """ id: str """Unique identifier for the credential.""" archived_at: Optional[datetime] = None """A timestamp in RFC 3339 format""" auth: Auth """Authentication details for a credential.""" created_at: datetime """A timestamp in RFC 3339 format""" metadata: Dict[str, str] """Arbitrary key-value metadata attached to the credential.""" type: Literal["vault_credential"] updated_at: datetime """A timestamp in RFC 3339 format""" vault_id: str """Identifier of the vault this credential belongs to.""" display_name: Optional[str] = None """Human-readable name for the credential.""" beta_managed_agents_credential_networking_params.py000066400000000000000000000012601523216435200363450ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from typing_extensions import TypeAlias from .beta_managed_agents_limited_credential_networking_params import BetaManagedAgentsLimitedCredentialNetworkingParams from .beta_managed_agents_unrestricted_credential_networking_params import ( BetaManagedAgentsUnrestrictedCredentialNetworkingParams, ) __all__ = ["BetaManagedAgentsCredentialNetworkingParams"] BetaManagedAgentsCredentialNetworkingParams: TypeAlias = Union[ BetaManagedAgentsUnrestrictedCredentialNetworkingParams, BetaManagedAgentsLimitedCredentialNetworkingParams ] beta_managed_agents_credential_validation.py000066400000000000000000000026221523216435200347500ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from datetime import datetime from typing_extensions import Literal from ...._models import BaseModel from .beta_managed_agents_mcp_probe import BetaManagedAgentsMCPProbe from .beta_managed_agents_refresh_object import BetaManagedAgentsRefreshObject from .beta_managed_agents_credential_validation_status import BetaManagedAgentsCredentialValidationStatus __all__ = ["BetaManagedAgentsCredentialValidation"] class BetaManagedAgentsCredentialValidation(BaseModel): """Result of live-probing a credential against its configured MCP server.""" credential_id: str """Unique identifier of the credential that was validated.""" has_refresh_token: bool """Whether the credential has a refresh token configured.""" mcp_probe: Optional[BetaManagedAgentsMCPProbe] = None """The failing step of an MCP validation probe.""" refresh: Optional[BetaManagedAgentsRefreshObject] = None """Outcome of a refresh-token exchange attempted during credential validation.""" status: BetaManagedAgentsCredentialValidationStatus """Overall verdict of a credential validation probe.""" type: Literal["vault_credential_validation"] validated_at: datetime """A timestamp in RFC 3339 format""" vault_id: str """Identifier of the vault containing the credential.""" beta_managed_agents_credential_validation_status.py000066400000000000000000000004441523216435200363530ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal, TypeAlias __all__ = ["BetaManagedAgentsCredentialValidationStatus"] BetaManagedAgentsCredentialValidationStatus: TypeAlias = Literal["valid", "invalid", "unknown"] beta_managed_agents_deleted_credential.py000066400000000000000000000006541523216435200342270ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsDeletedCredential"] class BetaManagedAgentsDeletedCredential(BaseModel): """Confirmation of a deleted credential.""" id: str """Unique identifier of the deleted credential.""" type: Literal["vault_credential_deleted"] beta_managed_agents_environment_variable_auth_response.py000066400000000000000000000026061523216435200375760ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Literal, Annotated, TypeAlias from ...._utils import PropertyInfo from ...._models import BaseModel from .beta_managed_agents_injection_location_response import BetaManagedAgentsInjectionLocationResponse from .beta_managed_agents_limited_credential_networking_response import ( BetaManagedAgentsLimitedCredentialNetworkingResponse, ) from .beta_managed_agents_unrestricted_credential_networking_response import ( BetaManagedAgentsUnrestrictedCredentialNetworkingResponse, ) __all__ = ["BetaManagedAgentsEnvironmentVariableAuthResponse", "Networking"] Networking: TypeAlias = Annotated[ Union[ BetaManagedAgentsUnrestrictedCredentialNetworkingResponse, BetaManagedAgentsLimitedCredentialNetworkingResponse ], PropertyInfo(discriminator="type"), ] class BetaManagedAgentsEnvironmentVariableAuthResponse(BaseModel): """Environment variable credential details. The secret value is never returned.""" injection_location: BetaManagedAgentsInjectionLocationResponse """Where in the outbound request the secret value is substituted.""" networking: Networking """Outbound hosts the secret value is substituted on.""" secret_name: str """Name of the environment variable.""" type: Literal["environment_variable"] beta_managed_agents_environment_variable_create_params.py000066400000000000000000000021531523216435200375220ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict from .beta_managed_agents_injection_location_params import BetaManagedAgentsInjectionLocationParams from .beta_managed_agents_credential_networking_params import BetaManagedAgentsCredentialNetworkingParams __all__ = ["BetaManagedAgentsEnvironmentVariableCreateParams"] class BetaManagedAgentsEnvironmentVariableCreateParams(TypedDict, total=False): """Parameters for creating an environment variable credential.""" networking: Required[BetaManagedAgentsCredentialNetworkingParams] """Outbound hosts the secret value is substituted on.""" secret_name: Required[str] """Name of the environment variable. Immutable after create.""" secret_value: Required[str] """Secret value. Write-only; never returned in responses.""" type: Required[Literal["environment_variable"]] injection_location: BetaManagedAgentsInjectionLocationParams """Where in the outbound request the secret value may be substituted.""" beta_managed_agents_environment_variable_update_params.py000066400000000000000000000020151523216435200375360ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict from .beta_managed_agents_credential_networking_params import BetaManagedAgentsCredentialNetworkingParams from .beta_managed_agents_injection_location_update_params import BetaManagedAgentsInjectionLocationUpdateParams __all__ = ["BetaManagedAgentsEnvironmentVariableUpdateParams"] class BetaManagedAgentsEnvironmentVariableUpdateParams(TypedDict, total=False): """Parameters for updating an environment variable credential. `secret_name` is immutable. """ type: Required[Literal["environment_variable"]] injection_location: BetaManagedAgentsInjectionLocationUpdateParams """Updated injection location.""" networking: Optional[BetaManagedAgentsCredentialNetworkingParams] """Updated networking scope. Full replacement.""" secret_value: Optional[str] """Updated secret value.""" beta_managed_agents_injection_location_params.py000066400000000000000000000010501523216435200356330ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import TypedDict __all__ = ["BetaManagedAgentsInjectionLocationParams"] class BetaManagedAgentsInjectionLocationParams(TypedDict, total=False): """Where in the outbound request the secret value may be substituted.""" body: bool """Substitute when the placeholder appears in the request body.""" header: bool """Substitute when the placeholder appears in a request header value.""" beta_managed_agents_injection_location_response.py000066400000000000000000000007561523216435200362220ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from ...._models import BaseModel __all__ = ["BetaManagedAgentsInjectionLocationResponse"] class BetaManagedAgentsInjectionLocationResponse(BaseModel): """Where in the outbound request the secret value is substituted.""" body: bool """Whether the placeholder is substituted in the request body.""" header: bool """Whether the placeholder is substituted in request header values.""" beta_managed_agents_injection_location_update_params.py000066400000000000000000000010151523216435200371760ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import TypedDict __all__ = ["BetaManagedAgentsInjectionLocationUpdateParams"] class BetaManagedAgentsInjectionLocationUpdateParams(TypedDict, total=False): """Updated injection location.""" body: bool """Substitute when the placeholder appears in the request body.""" header: bool """Substitute when the placeholder appears in a request header value.""" beta_managed_agents_limited_credential_networking_params.py000066400000000000000000000014601523216435200400560ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict from ...._types import SequenceNotStr __all__ = ["BetaManagedAgentsLimitedCredentialNetworkingParams"] class BetaManagedAgentsLimitedCredentialNetworkingParams(TypedDict, total=False): """Substitute the secret only on requests to the listed hosts.""" allowed_hosts: Required[SequenceNotStr[str]] """Hostnames on which the secret will be substituted. Each entry is a bare hostname (`api.example.com`), an IPv4 address (`192.0.2.1`), or a `*.`-prefixed wildcard (`*.example.com`). URLs, ports, paths, and IPv6 addresses are not accepted. At most 16 entries. """ type: Required[Literal["limited"]] beta_managed_agents_limited_credential_networking_response.py000066400000000000000000000012341523216435200404300ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsLimitedCredentialNetworkingResponse"] class BetaManagedAgentsLimitedCredentialNetworkingResponse(BaseModel): """The secret is substituted only on requests to the listed hosts.""" allowed_hosts: List[str] """Hostnames on which the secret will be substituted. An entry matches the request host exactly; a `*.`-prefixed entry matches any subdomain of the named domain but not the domain itself. """ type: Literal["limited"] beta_managed_agents_mcp_oauth_auth_response.py000066400000000000000000000015161523216435200353430ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from datetime import datetime from typing_extensions import Literal from ...._models import BaseModel from .beta_managed_agents_mcp_oauth_refresh_response import BetaManagedAgentsMCPOAuthRefreshResponse __all__ = ["BetaManagedAgentsMCPOAuthAuthResponse"] class BetaManagedAgentsMCPOAuthAuthResponse(BaseModel): """OAuth credential details for an MCP server.""" mcp_server_url: str """URL of the MCP server this credential authenticates against.""" type: Literal["mcp_oauth"] expires_at: Optional[datetime] = None """A timestamp in RFC 3339 format""" refresh: Optional[BetaManagedAgentsMCPOAuthRefreshResponse] = None """OAuth refresh token configuration returned in credential responses.""" beta_managed_agents_mcp_oauth_create_params.py000066400000000000000000000020521523216435200352660ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from datetime import datetime from typing_extensions import Literal, Required, Annotated, TypedDict from ...._utils import PropertyInfo from .beta_managed_agents_mcp_oauth_refresh_params import BetaManagedAgentsMCPOAuthRefreshParams __all__ = ["BetaManagedAgentsMCPOAuthCreateParams"] class BetaManagedAgentsMCPOAuthCreateParams(TypedDict, total=False): """Parameters for creating an MCP OAuth credential.""" access_token: Required[str] """OAuth access token.""" mcp_server_url: Required[str] """URL of the MCP server this credential authenticates against.""" type: Required[Literal["mcp_oauth"]] expires_at: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] """A timestamp in RFC 3339 format""" refresh: Optional[BetaManagedAgentsMCPOAuthRefreshParams] """OAuth refresh token parameters for creating a credential with refresh support.""" beta_managed_agents_mcp_oauth_refresh_params.py000066400000000000000000000026431523216435200354670ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from typing_extensions import Required, TypeAlias, TypedDict from .beta_managed_agents_token_endpoint_auth_none_param import BetaManagedAgentsTokenEndpointAuthNoneParam from .beta_managed_agents_token_endpoint_auth_post_param import BetaManagedAgentsTokenEndpointAuthPostParam from .beta_managed_agents_token_endpoint_auth_basic_param import BetaManagedAgentsTokenEndpointAuthBasicParam __all__ = ["BetaManagedAgentsMCPOAuthRefreshParams", "TokenEndpointAuth"] TokenEndpointAuth: TypeAlias = Union[ BetaManagedAgentsTokenEndpointAuthNoneParam, BetaManagedAgentsTokenEndpointAuthBasicParam, BetaManagedAgentsTokenEndpointAuthPostParam, ] class BetaManagedAgentsMCPOAuthRefreshParams(TypedDict, total=False): """OAuth refresh token parameters for creating a credential with refresh support.""" client_id: Required[str] """OAuth client ID.""" refresh_token: Required[str] """OAuth refresh token.""" token_endpoint: Required[str] """Token endpoint URL used to refresh the access token.""" token_endpoint_auth: Required[TokenEndpointAuth] """Token endpoint requires no client authentication.""" resource: Optional[str] """OAuth resource indicator.""" scope: Optional[str] """OAuth scope for the refresh request.""" beta_managed_agents_mcp_oauth_refresh_response.py000066400000000000000000000026731523216435200360450ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union, Optional from typing_extensions import Annotated, TypeAlias from ...._utils import PropertyInfo from ...._models import BaseModel from .beta_managed_agents_token_endpoint_auth_none_response import BetaManagedAgentsTokenEndpointAuthNoneResponse from .beta_managed_agents_token_endpoint_auth_post_response import BetaManagedAgentsTokenEndpointAuthPostResponse from .beta_managed_agents_token_endpoint_auth_basic_response import BetaManagedAgentsTokenEndpointAuthBasicResponse __all__ = ["BetaManagedAgentsMCPOAuthRefreshResponse", "TokenEndpointAuth"] TokenEndpointAuth: TypeAlias = Annotated[ Union[ BetaManagedAgentsTokenEndpointAuthNoneResponse, BetaManagedAgentsTokenEndpointAuthBasicResponse, BetaManagedAgentsTokenEndpointAuthPostResponse, ], PropertyInfo(discriminator="type"), ] class BetaManagedAgentsMCPOAuthRefreshResponse(BaseModel): """OAuth refresh token configuration returned in credential responses.""" client_id: str """OAuth client ID.""" token_endpoint: str """Token endpoint URL used to refresh the access token.""" token_endpoint_auth: TokenEndpointAuth """Token endpoint requires no client authentication.""" resource: Optional[str] = None """OAuth resource indicator.""" scope: Optional[str] = None """OAuth scope for the refresh request.""" beta_managed_agents_mcp_oauth_refresh_update_params.py000066400000000000000000000021331523216435200370230ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from typing_extensions import TypeAlias, TypedDict from .beta_managed_agents_token_endpoint_auth_post_update_param import BetaManagedAgentsTokenEndpointAuthPostUpdateParam from .beta_managed_agents_token_endpoint_auth_basic_update_param import ( BetaManagedAgentsTokenEndpointAuthBasicUpdateParam, ) __all__ = ["BetaManagedAgentsMCPOAuthRefreshUpdateParams", "TokenEndpointAuth"] TokenEndpointAuth: TypeAlias = Union[ BetaManagedAgentsTokenEndpointAuthBasicUpdateParam, BetaManagedAgentsTokenEndpointAuthPostUpdateParam ] class BetaManagedAgentsMCPOAuthRefreshUpdateParams(TypedDict, total=False): """Parameters for updating OAuth refresh token configuration.""" refresh_token: Optional[str] """Updated OAuth refresh token.""" scope: Optional[str] """Updated OAuth scope for the refresh request.""" token_endpoint_auth: TokenEndpointAuth """Updated HTTP Basic authentication parameters for the token endpoint.""" beta_managed_agents_mcp_oauth_update_params.py000066400000000000000000000017641523216435200353160ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from datetime import datetime from typing_extensions import Literal, Required, Annotated, TypedDict from ...._utils import PropertyInfo from .beta_managed_agents_mcp_oauth_refresh_update_params import BetaManagedAgentsMCPOAuthRefreshUpdateParams __all__ = ["BetaManagedAgentsMCPOAuthUpdateParams"] class BetaManagedAgentsMCPOAuthUpdateParams(TypedDict, total=False): """Parameters for updating an MCP OAuth credential. The `mcp_server_url` is immutable. """ type: Required[Literal["mcp_oauth"]] access_token: Optional[str] """Updated OAuth access token.""" expires_at: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")] """A timestamp in RFC 3339 format""" refresh: Optional[BetaManagedAgentsMCPOAuthRefreshUpdateParams] """Parameters for updating OAuth refresh token configuration.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults/beta_managed_agents_mcp_probe.py000066400000000000000000000011661523216435200324530ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from ...._models import BaseModel from .beta_managed_agents_refresh_http_response import BetaManagedAgentsRefreshHTTPResponse __all__ = ["BetaManagedAgentsMCPProbe"] class BetaManagedAgentsMCPProbe(BaseModel): """The failing step of an MCP validation probe.""" http_response: Optional[BetaManagedAgentsRefreshHTTPResponse] = None """An HTTP response captured during a credential validation probe.""" method: str """The MCP method that failed (for example `initialize` or `tools/list`).""" beta_managed_agents_refresh_http_response.py000066400000000000000000000011161523216435200350340ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from ...._models import BaseModel __all__ = ["BetaManagedAgentsRefreshHTTPResponse"] class BetaManagedAgentsRefreshHTTPResponse(BaseModel): """An HTTP response captured during a credential validation probe.""" body: str """Response body. May be truncated and has sensitive values scrubbed.""" body_truncated: bool """Whether `body` was truncated.""" content_type: str """Value of the `Content-Type` response header.""" status_code: int """HTTP status code.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults/beta_managed_agents_refresh_object.py000066400000000000000000000014121523216435200334630ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ...._models import BaseModel from .beta_managed_agents_refresh_http_response import BetaManagedAgentsRefreshHTTPResponse __all__ = ["BetaManagedAgentsRefreshObject"] class BetaManagedAgentsRefreshObject(BaseModel): """Outcome of a refresh-token exchange attempted during credential validation.""" http_response: Optional[BetaManagedAgentsRefreshHTTPResponse] = None """An HTTP response captured during a credential validation probe.""" status: Literal["succeeded", "failed", "connect_error", "no_refresh_token"] """Outcome of a refresh-token exchange attempted during credential validation.""" beta_managed_agents_static_bearer_auth_response.py000066400000000000000000000007371523216435200361770ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsStaticBearerAuthResponse"] class BetaManagedAgentsStaticBearerAuthResponse(BaseModel): """Static bearer token credential details for an MCP server.""" mcp_server_url: str """URL of the MCP server this credential authenticates against.""" type: Literal["static_bearer"] beta_managed_agents_static_bearer_create_params.py000066400000000000000000000011251523216435200361160ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsStaticBearerCreateParams"] class BetaManagedAgentsStaticBearerCreateParams(TypedDict, total=False): """Parameters for creating a static bearer token credential.""" token: Required[str] """Static bearer token value.""" mcp_server_url: Required[str] """URL of the MCP server this credential authenticates against.""" type: Required[Literal["static_bearer"]] beta_managed_agents_static_bearer_update_params.py000066400000000000000000000010741523216435200361400ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsStaticBearerUpdateParams"] class BetaManagedAgentsStaticBearerUpdateParams(TypedDict, total=False): """Parameters for updating a static bearer token credential. The `mcp_server_url` is immutable. """ type: Required[Literal["static_bearer"]] token: Optional[str] """Updated static bearer token value.""" beta_managed_agents_token_endpoint_auth_basic_param.py000066400000000000000000000010061523216435200370010ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsTokenEndpointAuthBasicParam"] class BetaManagedAgentsTokenEndpointAuthBasicParam(TypedDict, total=False): """Token endpoint uses HTTP Basic authentication with client credentials.""" client_secret: Required[str] """OAuth client secret.""" type: Required[Literal["client_secret_basic"]] beta_managed_agents_token_endpoint_auth_basic_response.py000066400000000000000000000006361523216435200375470ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsTokenEndpointAuthBasicResponse"] class BetaManagedAgentsTokenEndpointAuthBasicResponse(BaseModel): """Token endpoint uses HTTP Basic authentication with client credentials.""" type: Literal["client_secret_basic"] beta_managed_agents_token_endpoint_auth_basic_update_param.py000066400000000000000000000010641523216435200403470ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsTokenEndpointAuthBasicUpdateParam"] class BetaManagedAgentsTokenEndpointAuthBasicUpdateParam(TypedDict, total=False): """Updated HTTP Basic authentication parameters for the token endpoint.""" type: Required[Literal["client_secret_basic"]] client_secret: Optional[str] """Updated OAuth client secret.""" beta_managed_agents_token_endpoint_auth_none_param.py000066400000000000000000000006371523216435200366700ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsTokenEndpointAuthNoneParam"] class BetaManagedAgentsTokenEndpointAuthNoneParam(TypedDict, total=False): """Token endpoint requires no client authentication.""" type: Required[Literal["none"]] beta_managed_agents_token_endpoint_auth_none_response.py000066400000000000000000000005701523216435200374220ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsTokenEndpointAuthNoneResponse"] class BetaManagedAgentsTokenEndpointAuthNoneResponse(BaseModel): """Token endpoint requires no client authentication.""" type: Literal["none"] beta_managed_agents_token_endpoint_auth_post_param.py000066400000000000000000000010021523216435200367010ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsTokenEndpointAuthPostParam"] class BetaManagedAgentsTokenEndpointAuthPostParam(TypedDict, total=False): """Token endpoint uses POST body authentication with client credentials.""" client_secret: Required[str] """OAuth client secret.""" type: Required[Literal["client_secret_post"]] beta_managed_agents_token_endpoint_auth_post_response.py000066400000000000000000000006321523216435200374470ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsTokenEndpointAuthPostResponse"] class BetaManagedAgentsTokenEndpointAuthPostResponse(BaseModel): """Token endpoint uses POST body authentication with client credentials.""" type: Literal["client_secret_post"] beta_managed_agents_token_endpoint_auth_post_update_param.py000066400000000000000000000010601523216435200402470ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsTokenEndpointAuthPostUpdateParam"] class BetaManagedAgentsTokenEndpointAuthPostUpdateParam(TypedDict, total=False): """Updated POST body authentication parameters for the token endpoint.""" type: Required[Literal["client_secret_post"]] client_secret: Optional[str] """Updated OAuth client secret.""" beta_managed_agents_unrestricted_credential_networking_params.py000066400000000000000000000011121523216435200411340ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["BetaManagedAgentsUnrestrictedCredentialNetworkingParams"] class BetaManagedAgentsUnrestrictedCredentialNetworkingParams(TypedDict, total=False): """ Substitute the secret on any host the session's Environment network policy permits egress to. The Environment's network policy is the only boundary on where the secret can reach. """ type: Required[Literal["unrestricted"]] beta_managed_agents_unrestricted_credential_networking_response.py000066400000000000000000000007201523216435200415130ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ...._models import BaseModel __all__ = ["BetaManagedAgentsUnrestrictedCredentialNetworkingResponse"] class BetaManagedAgentsUnrestrictedCredentialNetworkingResponse(BaseModel): """ The secret is substituted on any host the session's Environment network policy permits egress to. """ type: Literal["unrestricted"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults/credential_create_params.py000066400000000000000000000026451523216435200315000ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Union, Optional from typing_extensions import Required, Annotated, TypeAlias, TypedDict from ...._utils import PropertyInfo from ...anthropic_beta_param import AnthropicBetaParam from .beta_managed_agents_mcp_oauth_create_params import BetaManagedAgentsMCPOAuthCreateParams from .beta_managed_agents_static_bearer_create_params import BetaManagedAgentsStaticBearerCreateParams from .beta_managed_agents_environment_variable_create_params import BetaManagedAgentsEnvironmentVariableCreateParams __all__ = ["CredentialCreateParams", "Auth"] class CredentialCreateParams(TypedDict, total=False): auth: Required[Auth] """Authentication details for creating a credential.""" display_name: Optional[str] """Human-readable name for the credential. Up to 255 characters.""" metadata: Dict[str, str] """Arbitrary key-value metadata to attach to the credential. Maximum 16 pairs, keys up to 64 chars, values up to 512 chars. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" Auth: TypeAlias = Union[ BetaManagedAgentsMCPOAuthCreateParams, BetaManagedAgentsStaticBearerCreateParams, BetaManagedAgentsEnvironmentVariableCreateParams, ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults/credential_list_params.py000066400000000000000000000015001523216435200311750ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List from typing_extensions import Annotated, TypedDict from ...._utils import PropertyInfo from ...anthropic_beta_param import AnthropicBetaParam __all__ = ["CredentialListParams"] class CredentialListParams(TypedDict, total=False): include_archived: bool """Whether to include archived credentials in the results.""" limit: int """Maximum number of credentials to return per page. Defaults to 20, maximum 100.""" page: str """Opaque pagination token from a previous `list_credentials` response.""" betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/beta/vaults/credential_update_params.py000066400000000000000000000027031523216435200315120ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Union, Optional from typing_extensions import Required, Annotated, TypeAlias, TypedDict from ...._utils import PropertyInfo from ...anthropic_beta_param import AnthropicBetaParam from .beta_managed_agents_mcp_oauth_update_params import BetaManagedAgentsMCPOAuthUpdateParams from .beta_managed_agents_static_bearer_update_params import BetaManagedAgentsStaticBearerUpdateParams from .beta_managed_agents_environment_variable_update_params import BetaManagedAgentsEnvironmentVariableUpdateParams __all__ = ["CredentialUpdateParams", "Auth"] class CredentialUpdateParams(TypedDict, total=False): vault_id: Required[str] auth: Auth """Updated authentication details for a credential.""" display_name: Optional[str] """Updated human-readable name for the credential. 1-255 characters.""" metadata: Optional[Dict[str, Optional[str]]] """Metadata patch. Set a key to a string to upsert it, or to null to delete it. Omitted keys are preserved. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" Auth: TypeAlias = Union[ BetaManagedAgentsMCPOAuthUpdateParams, BetaManagedAgentsStaticBearerUpdateParams, BetaManagedAgentsEnvironmentVariableUpdateParams, ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta_api_error.py000066400000000000000000000004141523216435200252140ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel __all__ = ["BetaAPIError"] class BetaAPIError(BaseModel): message: str type: Literal["api_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta_authentication_error.py000066400000000000000000000004551523216435200274670ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel __all__ = ["BetaAuthenticationError"] class BetaAuthenticationError(BaseModel): message: str type: Literal["authentication_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta_billing_error.py000066400000000000000000000004301523216435200260610ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel __all__ = ["BetaBillingError"] class BetaBillingError(BaseModel): message: str type: Literal["billing_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta_error.py000066400000000000000000000020631523216435200243650ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from .._utils import PropertyInfo from .beta_api_error import BetaAPIError from .beta_billing_error import BetaBillingError from .beta_not_found_error import BetaNotFoundError from .beta_overloaded_error import BetaOverloadedError from .beta_permission_error import BetaPermissionError from .beta_rate_limit_error import BetaRateLimitError from .beta_authentication_error import BetaAuthenticationError from .beta_gateway_timeout_error import BetaGatewayTimeoutError from .beta_invalid_request_error import BetaInvalidRequestError __all__ = ["BetaError"] BetaError: TypeAlias = Annotated[ Union[ BetaInvalidRequestError, BetaAuthenticationError, BetaBillingError, BetaPermissionError, BetaNotFoundError, BetaRateLimitError, BetaGatewayTimeoutError, BetaAPIError, BetaOverloadedError, ], PropertyInfo(discriminator="type"), ] anthropic-sdk-python-0.120.2/src/anthropic/types/beta_error_response.py000066400000000000000000000005721523216435200263060ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from .._models import BaseModel from .beta_error import BetaError __all__ = ["BetaErrorResponse"] class BetaErrorResponse(BaseModel): error: BetaError request_id: Optional[str] = None type: Literal["error"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta_gateway_timeout_error.py000066400000000000000000000004461523216435200276570ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel __all__ = ["BetaGatewayTimeoutError"] class BetaGatewayTimeoutError(BaseModel): message: str type: Literal["timeout_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta_invalid_request_error.py000066400000000000000000000004561523216435200276470ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel __all__ = ["BetaInvalidRequestError"] class BetaInvalidRequestError(BaseModel): message: str type: Literal["invalid_request_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta_not_found_error.py000066400000000000000000000004341523216435200264400ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel __all__ = ["BetaNotFoundError"] class BetaNotFoundError(BaseModel): message: str type: Literal["not_found_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta_overloaded_error.py000066400000000000000000000004411523216435200265670ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel __all__ = ["BetaOverloadedError"] class BetaOverloadedError(BaseModel): message: str type: Literal["overloaded_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta_permission_error.py000066400000000000000000000004411523216435200266330ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel __all__ = ["BetaPermissionError"] class BetaPermissionError(BaseModel): message: str type: Literal["permission_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/beta_rate_limit_error.py000066400000000000000000000004371523216435200266010ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel __all__ = ["BetaRateLimitError"] class BetaRateLimitError(BaseModel): message: str type: Literal["rate_limit_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/cache_control_ephemeral_param.py000066400000000000000000000012111523216435200302400ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["CacheControlEphemeralParam"] class CacheControlEphemeralParam(TypedDict, total=False): type: Required[Literal["ephemeral"]] ttl: Literal["5m", "1h"] """The time-to-live for the cache control breakpoint. This may be one the following values: - `5m`: 5 minutes - `1h`: 1 hour Defaults to `5m`. See [prompt caching pricing](https://platform.claude.com/docs/en/build-with-claude/prompt-caching) for details. """ anthropic-sdk-python-0.120.2/src/anthropic/types/cache_creation.py000066400000000000000000000006271523216435200251740ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .._models import BaseModel __all__ = ["CacheCreation"] class CacheCreation(BaseModel): ephemeral_1h_input_tokens: int """The number of input tokens used to create the 1 hour cache entry.""" ephemeral_5m_input_tokens: int """The number of input tokens used to create the 5 minute cache entry.""" anthropic-sdk-python-0.120.2/src/anthropic/types/capability_support.py000066400000000000000000000005071523216435200261570ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .._models import BaseModel __all__ = ["CapabilitySupport"] class CapabilitySupport(BaseModel): """Indicates whether a capability is supported.""" supported: bool """Whether this capability is supported by the model.""" anthropic-sdk-python-0.120.2/src/anthropic/types/citation_char_location.py000066400000000000000000000007311523216435200267400ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from .._models import BaseModel __all__ = ["CitationCharLocation"] class CitationCharLocation(BaseModel): cited_text: str document_index: int document_title: Optional[str] = None end_char_index: int file_id: Optional[str] = None start_char_index: int type: Literal["char_location"] anthropic-sdk-python-0.120.2/src/anthropic/types/citation_char_location_param.py000066400000000000000000000010321523216435200301130ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["CitationCharLocationParam"] class CitationCharLocationParam(TypedDict, total=False): cited_text: Required[str] document_index: Required[int] document_title: Required[Optional[str]] end_char_index: Required[int] start_char_index: Required[int] type: Required[Literal["char_location"]] anthropic-sdk-python-0.120.2/src/anthropic/types/citation_content_block_location.py000066400000000000000000000022501523216435200306450ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from .._models import BaseModel __all__ = ["CitationContentBlockLocation"] class CitationContentBlockLocation(BaseModel): cited_text: str """The full text of the cited block range, concatenated. Always equals the contents of `content[start_block_index:end_block_index]` joined together. The text block is the minimal citable unit; this field is never a substring of a single block. Not counted toward output tokens, and not counted toward input tokens when sent back in subsequent turns. """ document_index: int document_title: Optional[str] = None end_block_index: int """ Exclusive 0-based end index of the cited block range in the source's `content` array. Always greater than `start_block_index`; a single-block citation has `end_block_index = start_block_index + 1`. """ file_id: Optional[str] = None start_block_index: int """0-based index of the first cited block in the source's `content` array.""" type: Literal["content_block_location"] anthropic-sdk-python-0.120.2/src/anthropic/types/citation_content_block_location_param.py000066400000000000000000000023511523216435200320270ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["CitationContentBlockLocationParam"] class CitationContentBlockLocationParam(TypedDict, total=False): cited_text: Required[str] """The full text of the cited block range, concatenated. Always equals the contents of `content[start_block_index:end_block_index]` joined together. The text block is the minimal citable unit; this field is never a substring of a single block. Not counted toward output tokens, and not counted toward input tokens when sent back in subsequent turns. """ document_index: Required[int] document_title: Required[Optional[str]] end_block_index: Required[int] """ Exclusive 0-based end index of the cited block range in the source's `content` array. Always greater than `start_block_index`; a single-block citation has `end_block_index = start_block_index + 1`. """ start_block_index: Required[int] """0-based index of the first cited block in the source's `content` array.""" type: Required[Literal["content_block_location"]] anthropic-sdk-python-0.120.2/src/anthropic/types/citation_page_location.py000066400000000000000000000007331523216435200267410ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from .._models import BaseModel __all__ = ["CitationPageLocation"] class CitationPageLocation(BaseModel): cited_text: str document_index: int document_title: Optional[str] = None end_page_number: int file_id: Optional[str] = None start_page_number: int type: Literal["page_location"] anthropic-sdk-python-0.120.2/src/anthropic/types/citation_page_location_param.py000066400000000000000000000010341523216435200301140ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["CitationPageLocationParam"] class CitationPageLocationParam(TypedDict, total=False): cited_text: Required[str] document_index: Required[int] document_title: Required[Optional[str]] end_page_number: Required[int] start_page_number: Required[int] type: Required[Literal["page_location"]] anthropic-sdk-python-0.120.2/src/anthropic/types/citation_search_result_location_param.py000066400000000000000000000030531523216435200320460ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["CitationSearchResultLocationParam"] class CitationSearchResultLocationParam(TypedDict, total=False): cited_text: Required[str] """The full text of the cited block range, concatenated. Always equals the contents of `content[start_block_index:end_block_index]` joined together. The text block is the minimal citable unit; this field is never a substring of a single block. Not counted toward output tokens, and not counted toward input tokens when sent back in subsequent turns. """ end_block_index: Required[int] """ Exclusive 0-based end index of the cited block range in the source's `content` array. Always greater than `start_block_index`; a single-block citation has `end_block_index = start_block_index + 1`. """ search_result_index: Required[int] """ 0-based index of the cited search result among all `search_result` content blocks in the request, in the order they appear across messages and tool results. Counted separately from `document_index`; server-side web search results are not included in this count. """ source: Required[str] start_block_index: Required[int] """0-based index of the first cited block in the source's `content` array.""" title: Required[Optional[str]] type: Required[Literal["search_result_location"]] anthropic-sdk-python-0.120.2/src/anthropic/types/citation_web_search_result_location_param.py000066400000000000000000000010051523216435200326760ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["CitationWebSearchResultLocationParam"] class CitationWebSearchResultLocationParam(TypedDict, total=False): cited_text: Required[str] encrypted_index: Required[str] title: Required[Optional[str]] type: Required[Literal["web_search_result_location"]] url: Required[str] anthropic-sdk-python-0.120.2/src/anthropic/types/citations_config.py000066400000000000000000000003141523216435200255600ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .._models import BaseModel __all__ = ["CitationsConfig"] class CitationsConfig(BaseModel): enabled: bool anthropic-sdk-python-0.120.2/src/anthropic/types/citations_config_param.py000066400000000000000000000004171523216435200267440ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import TypedDict __all__ = ["CitationsConfigParam"] class CitationsConfigParam(TypedDict, total=False): enabled: bool anthropic-sdk-python-0.120.2/src/anthropic/types/citations_delta.py000066400000000000000000000017451523216435200254150ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Literal, Annotated, TypeAlias from .._utils import PropertyInfo from .._models import BaseModel from .citation_char_location import CitationCharLocation from .citation_page_location import CitationPageLocation from .citation_content_block_location import CitationContentBlockLocation from .citations_search_result_location import CitationsSearchResultLocation from .citations_web_search_result_location import CitationsWebSearchResultLocation __all__ = ["CitationsDelta", "Citation"] Citation: TypeAlias = Annotated[ Union[ CitationCharLocation, CitationPageLocation, CitationContentBlockLocation, CitationsWebSearchResultLocation, CitationsSearchResultLocation, ], PropertyInfo(discriminator="type"), ] class CitationsDelta(BaseModel): citation: Citation type: Literal["citations_delta"] anthropic-sdk-python-0.120.2/src/anthropic/types/citations_search_result_location.py000066400000000000000000000026771523216435200310640ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from .._models import BaseModel __all__ = ["CitationsSearchResultLocation"] class CitationsSearchResultLocation(BaseModel): cited_text: str """The full text of the cited block range, concatenated. Always equals the contents of `content[start_block_index:end_block_index]` joined together. The text block is the minimal citable unit; this field is never a substring of a single block. Not counted toward output tokens, and not counted toward input tokens when sent back in subsequent turns. """ end_block_index: int """ Exclusive 0-based end index of the cited block range in the source's `content` array. Always greater than `start_block_index`; a single-block citation has `end_block_index = start_block_index + 1`. """ search_result_index: int """ 0-based index of the cited search result among all `search_result` content blocks in the request, in the order they appear across messages and tool results. Counted separately from `document_index`; server-side web search results are not included in this count. """ source: str start_block_index: int """0-based index of the first cited block in the source's `content` array.""" title: Optional[str] = None type: Literal["search_result_location"] anthropic-sdk-python-0.120.2/src/anthropic/types/citations_web_search_result_location.py000066400000000000000000000006551523216435200317130ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from .._models import BaseModel __all__ = ["CitationsWebSearchResultLocation"] class CitationsWebSearchResultLocation(BaseModel): cited_text: str encrypted_index: str title: Optional[str] = None type: Literal["web_search_result_location"] url: str anthropic-sdk-python-0.120.2/src/anthropic/types/code_execution_output_block.py000066400000000000000000000004601523216435200300270ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel __all__ = ["CodeExecutionOutputBlock"] class CodeExecutionOutputBlock(BaseModel): file_id: str type: Literal["code_execution_output"] anthropic-sdk-python-0.120.2/src/anthropic/types/code_execution_output_block_param.py000066400000000000000000000005631523216435200312130ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["CodeExecutionOutputBlockParam"] class CodeExecutionOutputBlockParam(TypedDict, total=False): file_id: Required[str] type: Required[Literal["code_execution_output"]] anthropic-sdk-python-0.120.2/src/anthropic/types/code_execution_result_block.py000066400000000000000000000007351523216435200300120ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List from typing_extensions import Literal from .._models import BaseModel from .code_execution_output_block import CodeExecutionOutputBlock __all__ = ["CodeExecutionResultBlock"] class CodeExecutionResultBlock(BaseModel): content: List[CodeExecutionOutputBlock] return_code: int stderr: str stdout: str type: Literal["code_execution_result"] anthropic-sdk-python-0.120.2/src/anthropic/types/code_execution_result_block_param.py000066400000000000000000000011271523216435200311660ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Iterable from typing_extensions import Literal, Required, TypedDict from .code_execution_output_block_param import CodeExecutionOutputBlockParam __all__ = ["CodeExecutionResultBlockParam"] class CodeExecutionResultBlockParam(TypedDict, total=False): content: Required[Iterable[CodeExecutionOutputBlockParam]] return_code: Required[int] stderr: Required[str] stdout: Required[str] type: Required[Literal["code_execution_result"]] anthropic-sdk-python-0.120.2/src/anthropic/types/code_execution_tool_20250522_param.py000066400000000000000000000021631523216435200304350ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Literal, Required, TypedDict from .cache_control_ephemeral_param import CacheControlEphemeralParam __all__ = ["CodeExecutionTool20250522Param"] class CodeExecutionTool20250522Param(TypedDict, total=False): name: Required[Literal["code_execution"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["code_execution_20250522"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/code_execution_tool_20250825_param.py000066400000000000000000000021631523216435200304430ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Literal, Required, TypedDict from .cache_control_ephemeral_param import CacheControlEphemeralParam __all__ = ["CodeExecutionTool20250825Param"] class CodeExecutionTool20250825Param(TypedDict, total=False): name: Required[Literal["code_execution"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["code_execution_20250825"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/code_execution_tool_20260120_param.py000066400000000000000000000023331523216435200304270ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Literal, Required, TypedDict from .cache_control_ephemeral_param import CacheControlEphemeralParam __all__ = ["CodeExecutionTool20260120Param"] class CodeExecutionTool20260120Param(TypedDict, total=False): """ Code execution tool with REPL state persistence (daemon mode + gVisor checkpoint). """ name: Required[Literal["code_execution"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["code_execution_20260120"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/code_execution_tool_20260521_param.py000066400000000000000000000022571523216435200304410ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Literal, Required, TypedDict from .cache_control_ephemeral_param import CacheControlEphemeralParam __all__ = ["CodeExecutionTool20260521Param"] class CodeExecutionTool20260521Param(TypedDict, total=False): """Code execution tool with REPL state persistence.""" name: Required[Literal["code_execution"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["code_execution_20260521"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/code_execution_tool_result_block.py000066400000000000000000000010411523216435200310360ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel from .code_execution_tool_result_block_content import CodeExecutionToolResultBlockContent __all__ = ["CodeExecutionToolResultBlock"] class CodeExecutionToolResultBlock(BaseModel): content: CodeExecutionToolResultBlockContent """Code execution result with encrypted stdout for PFC + web_search results.""" tool_use_id: str type: Literal["code_execution_tool_result"] anthropic-sdk-python-0.120.2/src/anthropic/types/code_execution_tool_result_block_content.py000066400000000000000000000011071523216435200325730ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import TypeAlias from .code_execution_result_block import CodeExecutionResultBlock from .code_execution_tool_result_error import CodeExecutionToolResultError from .encrypted_code_execution_result_block import EncryptedCodeExecutionResultBlock __all__ = ["CodeExecutionToolResultBlockContent"] CodeExecutionToolResultBlockContent: TypeAlias = Union[ CodeExecutionToolResultError, CodeExecutionResultBlock, EncryptedCodeExecutionResultBlock ] anthropic-sdk-python-0.120.2/src/anthropic/types/code_execution_tool_result_block_param.py000066400000000000000000000015551523216435200322300ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict from .cache_control_ephemeral_param import CacheControlEphemeralParam from .code_execution_tool_result_block_param_content_param import CodeExecutionToolResultBlockParamContentParam __all__ = ["CodeExecutionToolResultBlockParam"] class CodeExecutionToolResultBlockParam(TypedDict, total=False): content: Required[CodeExecutionToolResultBlockParamContentParam] """Code execution result with encrypted stdout for PFC + web_search results.""" tool_use_id: Required[str] type: Required[Literal["code_execution_tool_result"]] cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" code_execution_tool_result_block_param_content_param.py000066400000000000000000000012571523216435200350620ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from typing_extensions import TypeAlias from .code_execution_result_block_param import CodeExecutionResultBlockParam from .code_execution_tool_result_error_param import CodeExecutionToolResultErrorParam from .encrypted_code_execution_result_block_param import EncryptedCodeExecutionResultBlockParam __all__ = ["CodeExecutionToolResultBlockParamContentParam"] CodeExecutionToolResultBlockParamContentParam: TypeAlias = Union[ CodeExecutionToolResultErrorParam, CodeExecutionResultBlockParam, EncryptedCodeExecutionResultBlockParam ] anthropic-sdk-python-0.120.2/src/anthropic/types/code_execution_tool_result_error.py000066400000000000000000000006671523216435200311120ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel from .code_execution_tool_result_error_code import CodeExecutionToolResultErrorCode __all__ = ["CodeExecutionToolResultError"] class CodeExecutionToolResultError(BaseModel): error_code: CodeExecutionToolResultErrorCode type: Literal["code_execution_tool_result_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/code_execution_tool_result_error_code.py000066400000000000000000000005121523216435200320710ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal, TypeAlias __all__ = ["CodeExecutionToolResultErrorCode"] CodeExecutionToolResultErrorCode: TypeAlias = Literal[ "invalid_tool_input", "unavailable", "too_many_requests", "execution_time_exceeded" ] anthropic-sdk-python-0.120.2/src/anthropic/types/code_execution_tool_result_error_param.py000066400000000000000000000007731523216435200322700ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict from .code_execution_tool_result_error_code import CodeExecutionToolResultErrorCode __all__ = ["CodeExecutionToolResultErrorParam"] class CodeExecutionToolResultErrorParam(TypedDict, total=False): error_code: Required[CodeExecutionToolResultErrorCode] type: Required[Literal["code_execution_tool_result_error"]] anthropic-sdk-python-0.120.2/src/anthropic/types/completion.py000066400000000000000000000021701523216435200244110ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from .model import Model from .._models import BaseModel __all__ = ["Completion"] class Completion(BaseModel): id: str """Unique object identifier. The format and length of IDs may change over time. """ completion: str """The resulting completion up to and excluding the stop sequences.""" model: Model """The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. """ stop_reason: Optional[str] = None """The reason that we stopped. This may be one the following values: - `"stop_sequence"`: we reached a stop sequence — either provided by you via the `stop_sequences` parameter, or a stop sequence built into the model - `"max_tokens"`: we exceeded `max_tokens_to_sample` or the model's maximum """ type: Literal["completion"] """Object type. For Text Completions, this is always `"completion"`. """ anthropic-sdk-python-0.120.2/src/anthropic/types/completion_create_params.py000066400000000000000000000112641523216435200273030ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Union from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict from .._types import SequenceNotStr from .._utils import PropertyInfo from .model_param import ModelParam from .metadata_param import MetadataParam from .anthropic_beta_param import AnthropicBetaParam __all__ = [ "CompletionRequestStreamingMetadata", "CompletionRequestNonStreamingMetadata", "CompletionRequestNonStreaming", "CompletionRequestStreaming", "CompletionCreateParamsBase", "Metadata", "CompletionCreateParamsNonStreaming", "CompletionCreateParamsStreaming", ] class CompletionCreateParamsBase(TypedDict, total=False): max_tokens_to_sample: Required[int] """The maximum number of tokens to generate before stopping. Note that our models may stop _before_ reaching this maximum. This parameter only specifies the absolute maximum number of tokens to generate. """ model: Required[ModelParam] """The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. """ prompt: Required[str] """The prompt that you want Claude to complete. For proper response generation you will need to format your prompt using alternating `\n\nHuman:` and `\n\nAssistant:` conversational turns. For example: ``` "\n\nHuman: {userQuestion}\n\nAssistant:" ``` See [prompt validation](https://platform.claude.com/docs/en/build-with-claude/working-with-messages) and our guide to [prompt design](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/overview) for more details. """ metadata: MetadataParam """An object describing metadata about the request.""" stop_sequences: SequenceNotStr[str] """Sequences that will cause the model to stop generating. Our models stop on `"\n\nHuman:"`, and may include additional built-in stop sequences in the future. By providing the stop_sequences parameter, you may include additional strings that will cause the model to stop generating. """ temperature: float """Amount of randomness injected into the response. Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` for analytical / multiple choice, and closer to `1.0` for creative and generative tasks. Note that even with `temperature` of `0.0`, the results will not be fully deterministic. """ top_k: int """Only sample from the top K options for each subsequent token. Used to remove "long tail" low probability responses. [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). Recommended for advanced use cases only. """ top_p: float """Use nucleus sampling. In nucleus sampling, we compute the cumulative distribution over all the options for each subsequent token in decreasing probability order and cut it off once it reaches a particular probability specified by `top_p`. Recommended for advanced use cases only. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" Metadata: TypeAlias = MetadataParam """This is deprecated, `MetadataParam` should be used instead""" class CompletionCreateParamsNonStreaming(CompletionCreateParamsBase, total=False): stream: Literal[False] """Whether to incrementally stream the response using server-sent events. See [streaming](https://platform.claude.com/docs/en/build-with-claude/streaming) for details. """ class CompletionCreateParamsStreaming(CompletionCreateParamsBase): stream: Required[Literal[True]] """Whether to incrementally stream the response using server-sent events. See [streaming](https://platform.claude.com/docs/en/build-with-claude/streaming) for details. """ CompletionRequestStreamingMetadata = MetadataParam """This is deprecated, `MetadataParam` should be used instead""" CompletionRequestNonStreamingMetadata = MetadataParam """This is deprecated, `MetadataParam` should be used instead""" CompletionRequestNonStreaming = CompletionCreateParamsNonStreaming """This is deprecated, `CompletionCreateParamsNonStreaming` should be used instead""" CompletionRequestStreaming = CompletionCreateParamsStreaming """This is deprecated, `CompletionCreateParamsStreaming` should be used instead""" CompletionCreateParams = Union[CompletionCreateParamsNonStreaming, CompletionCreateParamsStreaming] anthropic-sdk-python-0.120.2/src/anthropic/types/container.py000066400000000000000000000007151523216435200242250ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from datetime import datetime from .._models import BaseModel __all__ = ["Container"] class Container(BaseModel): """ Information about the container used in the request (for the code execution tool) """ id: str """Identifier for the container used in this request""" expires_at: datetime """The time at which the container will expire.""" anthropic-sdk-python-0.120.2/src/anthropic/types/container_upload_block.py000066400000000000000000000005431523216435200267420ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel __all__ = ["ContainerUploadBlock"] class ContainerUploadBlock(BaseModel): """Response model for a file uploaded to the container.""" file_id: str type: Literal["container_upload"] anthropic-sdk-python-0.120.2/src/anthropic/types/container_upload_block_param.py000066400000000000000000000013711523216435200301220ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict from .cache_control_ephemeral_param import CacheControlEphemeralParam __all__ = ["ContainerUploadBlockParam"] class ContainerUploadBlockParam(TypedDict, total=False): """ A content block that represents a file to be uploaded to the container Files uploaded via this block will be available in the container's input directory. """ file_id: Required[str] type: Required[Literal["container_upload"]] cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" anthropic-sdk-python-0.120.2/src/anthropic/types/content_block.py000066400000000000000000000026531523216435200250720ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from .._utils import PropertyInfo from .text_block import TextBlock from .thinking_block import ThinkingBlock from .tool_use_block import ToolUseBlock from .server_tool_use_block import ServerToolUseBlock from .container_upload_block import ContainerUploadBlock from .redacted_thinking_block import RedactedThinkingBlock from .web_fetch_tool_result_block import WebFetchToolResultBlock from .web_search_tool_result_block import WebSearchToolResultBlock from .tool_search_tool_result_block import ToolSearchToolResultBlock from .code_execution_tool_result_block import CodeExecutionToolResultBlock from .bash_code_execution_tool_result_block import BashCodeExecutionToolResultBlock from .text_editor_code_execution_tool_result_block import TextEditorCodeExecutionToolResultBlock __all__ = ["ContentBlock"] ContentBlock: TypeAlias = Annotated[ Union[ TextBlock, ThinkingBlock, RedactedThinkingBlock, ToolUseBlock, ServerToolUseBlock, WebSearchToolResultBlock, WebFetchToolResultBlock, CodeExecutionToolResultBlock, BashCodeExecutionToolResultBlock, TextEditorCodeExecutionToolResultBlock, ToolSearchToolResultBlock, ContainerUploadBlock, ], PropertyInfo(discriminator="type"), ] anthropic-sdk-python-0.120.2/src/anthropic/types/content_block_delta_event.py000066400000000000000000000004661523216435200274440ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .raw_content_block_delta_event import RawContentBlockDeltaEvent __all__ = ["ContentBlockDeltaEvent"] ContentBlockDeltaEvent = RawContentBlockDeltaEvent """The RawContentBlockDeltaEvent type should be used instead""" anthropic-sdk-python-0.120.2/src/anthropic/types/content_block_param.py000066400000000000000000000036631523216435200262540ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from typing_extensions import TypeAlias from .text_block_param import TextBlockParam from .image_block_param import ImageBlockParam from .document_block_param import DocumentBlockParam from .thinking_block_param import ThinkingBlockParam from .tool_use_block_param import ToolUseBlockParam from .tool_result_block_param import ToolResultBlockParam from .search_result_block_param import SearchResultBlockParam from .server_tool_use_block_param import ServerToolUseBlockParam from .container_upload_block_param import ContainerUploadBlockParam from .redacted_thinking_block_param import RedactedThinkingBlockParam from .web_fetch_tool_result_block_param import WebFetchToolResultBlockParam from .web_search_tool_result_block_param import WebSearchToolResultBlockParam from .mid_conversation_system_block_param import MidConversationSystemBlockParam from .tool_search_tool_result_block_param import ToolSearchToolResultBlockParam from .code_execution_tool_result_block_param import CodeExecutionToolResultBlockParam from .bash_code_execution_tool_result_block_param import BashCodeExecutionToolResultBlockParam from .text_editor_code_execution_tool_result_block_param import TextEditorCodeExecutionToolResultBlockParam __all__ = ["ContentBlockParam"] ContentBlockParam: TypeAlias = Union[ TextBlockParam, ImageBlockParam, DocumentBlockParam, SearchResultBlockParam, ThinkingBlockParam, RedactedThinkingBlockParam, ToolUseBlockParam, ToolResultBlockParam, ServerToolUseBlockParam, WebSearchToolResultBlockParam, WebFetchToolResultBlockParam, CodeExecutionToolResultBlockParam, BashCodeExecutionToolResultBlockParam, TextEditorCodeExecutionToolResultBlockParam, ToolSearchToolResultBlockParam, ContainerUploadBlockParam, MidConversationSystemBlockParam, ] anthropic-sdk-python-0.120.2/src/anthropic/types/content_block_source_content_param.py000066400000000000000000000006331523216435200313600ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from typing_extensions import TypeAlias from .text_block_param import TextBlockParam from .image_block_param import ImageBlockParam __all__ = ["ContentBlockSourceContentParam"] ContentBlockSourceContentParam: TypeAlias = Union[TextBlockParam, ImageBlockParam] anthropic-sdk-python-0.120.2/src/anthropic/types/content_block_source_param.py000066400000000000000000000007751523216435200276350ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Iterable from typing_extensions import Literal, Required, TypedDict from .content_block_source_content_param import ContentBlockSourceContentParam __all__ = ["ContentBlockSourceParam"] class ContentBlockSourceParam(TypedDict, total=False): content: Required[Union[str, Iterable[ContentBlockSourceContentParam]]] type: Required[Literal["content"]] anthropic-sdk-python-0.120.2/src/anthropic/types/content_block_start_event.py000066400000000000000000000004661523216435200275100ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .raw_content_block_start_event import RawContentBlockStartEvent __all__ = ["ContentBlockStartEvent"] ContentBlockStartEvent = RawContentBlockStartEvent """The RawContentBlockStartEvent type should be used instead""" anthropic-sdk-python-0.120.2/src/anthropic/types/content_block_stop_event.py000066400000000000000000000004601523216435200273320ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .raw_content_block_stop_event import RawContentBlockStopEvent __all__ = ["ContentBlockStopEvent"] ContentBlockStopEvent = RawContentBlockStopEvent """The RawContentBlockStopEvent type should be used instead""" anthropic-sdk-python-0.120.2/src/anthropic/types/context_management_capability.py000066400000000000000000000014061523216435200303220ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from .._models import BaseModel from .capability_support import CapabilitySupport __all__ = ["ContextManagementCapability"] class ContextManagementCapability(BaseModel): """Context management capability details.""" clear_thinking_20251015: Optional[CapabilitySupport] = None """Indicates whether a capability is supported.""" clear_tool_uses_20250919: Optional[CapabilitySupport] = None """Indicates whether a capability is supported.""" compact_20260112: Optional[CapabilitySupport] = None """Indicates whether a capability is supported.""" supported: bool """Whether this capability is supported by the model.""" anthropic-sdk-python-0.120.2/src/anthropic/types/direct_caller.py000066400000000000000000000004531523216435200250360ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel __all__ = ["DirectCaller"] class DirectCaller(BaseModel): """Tool invocation directly from the model.""" type: Literal["direct"] anthropic-sdk-python-0.120.2/src/anthropic/types/direct_caller_param.py000066400000000000000000000005441523216435200262170ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["DirectCallerParam"] class DirectCallerParam(TypedDict, total=False): """Tool invocation directly from the model.""" type: Required[Literal["direct"]] anthropic-sdk-python-0.120.2/src/anthropic/types/document_block.py000066400000000000000000000014241523216435200252310ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union, Optional from typing_extensions import Literal, Annotated, TypeAlias from .._utils import PropertyInfo from .._models import BaseModel from .citations_config import CitationsConfig from .base64_pdf_source import Base64PDFSource from .plain_text_source import PlainTextSource __all__ = ["DocumentBlock", "Source"] Source: TypeAlias = Annotated[Union[Base64PDFSource, PlainTextSource], PropertyInfo(discriminator="type")] class DocumentBlock(BaseModel): citations: Optional[CitationsConfig] = None """Citation configuration for the document""" source: Source title: Optional[str] = None """The title of the document""" type: Literal["document"] anthropic-sdk-python-0.120.2/src/anthropic/types/document_block_param.py000066400000000000000000000021061523216435200264070ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .url_pdf_source_param import URLPDFSourceParam from .citations_config_param import CitationsConfigParam from .base64_pdf_source_param import Base64PDFSourceParam from .plain_text_source_param import PlainTextSourceParam from .content_block_source_param import ContentBlockSourceParam from .cache_control_ephemeral_param import CacheControlEphemeralParam __all__ = ["DocumentBlockParam", "Source"] Source: TypeAlias = Union[Base64PDFSourceParam, PlainTextSourceParam, ContentBlockSourceParam, URLPDFSourceParam] class DocumentBlockParam(TypedDict, total=False): source: Required[Source] type: Required[Literal["document"]] cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" citations: Optional[CitationsConfigParam] context: Optional[str] title: Optional[str] anthropic-sdk-python-0.120.2/src/anthropic/types/effort_capability.py000066400000000000000000000015171523216435200257320ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from .._models import BaseModel from .capability_support import CapabilitySupport __all__ = ["EffortCapability"] class EffortCapability(BaseModel): """Effort (reasoning_effort) capability details.""" high: CapabilitySupport """Whether the model supports high effort level.""" low: CapabilitySupport """Whether the model supports low effort level.""" max: CapabilitySupport """Whether the model supports max effort level.""" medium: CapabilitySupport """Whether the model supports medium effort level.""" supported: bool """Whether this capability is supported by the model.""" xhigh: Optional[CapabilitySupport] = None """Indicates whether a capability is supported.""" anthropic-sdk-python-0.120.2/src/anthropic/types/encrypted_code_execution_result_block.py000066400000000000000000000011301523216435200320550ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List from typing_extensions import Literal from .._models import BaseModel from .code_execution_output_block import CodeExecutionOutputBlock __all__ = ["EncryptedCodeExecutionResultBlock"] class EncryptedCodeExecutionResultBlock(BaseModel): """Code execution result with encrypted stdout for PFC + web_search results.""" content: List[CodeExecutionOutputBlock] encrypted_stdout: str return_code: int stderr: str type: Literal["encrypted_code_execution_result"] anthropic-sdk-python-0.120.2/src/anthropic/types/encrypted_code_execution_result_block_param.py000066400000000000000000000013221523216435200332400ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Iterable from typing_extensions import Literal, Required, TypedDict from .code_execution_output_block_param import CodeExecutionOutputBlockParam __all__ = ["EncryptedCodeExecutionResultBlockParam"] class EncryptedCodeExecutionResultBlockParam(TypedDict, total=False): """Code execution result with encrypted stdout for PFC + web_search results.""" content: Required[Iterable[CodeExecutionOutputBlockParam]] encrypted_stdout: Required[str] return_code: Required[int] stderr: Required[str] type: Required[Literal["encrypted_code_execution_result"]] anthropic-sdk-python-0.120.2/src/anthropic/types/image_block_param.py000066400000000000000000000014021523216435200256510ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .url_image_source_param import URLImageSourceParam from .base64_image_source_param import Base64ImageSourceParam from .cache_control_ephemeral_param import CacheControlEphemeralParam __all__ = ["ImageBlockParam", "Source"] Source: TypeAlias = Union[Base64ImageSourceParam, URLImageSourceParam] class ImageBlockParam(TypedDict, total=False): source: Required[Source] type: Required[Literal["image"]] cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" anthropic-sdk-python-0.120.2/src/anthropic/types/input_json_delta.py000066400000000000000000000005201523216435200255760ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel __all__ = ["InputJSONDelta", "InputJsonDelta"] class InputJSONDelta(BaseModel): partial_json: str type: Literal["input_json_delta"] InputJsonDelta = InputJSONDelta anthropic-sdk-python-0.120.2/src/anthropic/types/json_output_format_param.py000066400000000000000000000006461523216435200273670ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict from typing_extensions import Literal, Required, TypedDict __all__ = ["JSONOutputFormatParam"] class JSONOutputFormatParam(TypedDict, total=False): schema: Required[Dict[str, object]] """The JSON schema of the format""" type: Required[Literal["json_schema"]] anthropic-sdk-python-0.120.2/src/anthropic/types/memory_tool_20250818_param.py000066400000000000000000000022261523216435200267600ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Iterable, Optional from typing_extensions import Literal, Required, TypedDict from .cache_control_ephemeral_param import CacheControlEphemeralParam __all__ = ["MemoryTool20250818Param"] class MemoryTool20250818Param(TypedDict, total=False): name: Required[Literal["memory"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["memory_20250818"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ input_examples: Iterable[Dict[str, object]] strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/message.py000066400000000000000000000075471523216435200237010ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from typing_extensions import Literal from .model import Model from .usage import Usage from .._models import BaseModel from .container import Container from .stop_reason import StopReason from .content_block import ContentBlock, ContentBlock as ContentBlock from .refusal_stop_details import RefusalStopDetails __all__ = ["Message"] class Message(BaseModel): id: str """Unique object identifier. The format and length of IDs may change over time. """ container: Optional[Container] = None """ Information about the container used in the request (for the code execution tool) """ content: List[ContentBlock] """Content generated by the model. This is an array of content blocks, each of which has a `type` that determines its shape. Example: ```json [{ "type": "text", "text": "Hi, I'm Claude." }] ``` If the request input `messages` ended with an `assistant` turn, then the response `content` will continue directly from that last turn. You can use this to constrain the model's output. For example, if the input `messages` were: ```json [ { "role": "user", "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" }, { "role": "assistant", "content": "The best answer is (" } ] ``` Then the response `content` might be: ```json [{ "type": "text", "text": "B)" }] ``` """ model: Model """The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. """ role: Literal["assistant"] """Conversational role of the generated message. This will always be `"assistant"`. """ stop_details: Optional[RefusalStopDetails] = None """Structured information about a refusal.""" stop_reason: Optional[StopReason] = None """The reason that we stopped. This may be one the following values: - `"end_turn"`: the model reached a natural stopping point - `"max_tokens"`: we exceeded the requested `max_tokens` or the model's maximum - `"stop_sequence"`: one of your provided custom `stop_sequences` was generated - `"tool_use"`: the model invoked one or more tools - `"pause_turn"`: we paused a long-running turn. You may provide the response back as-is in a subsequent request to let the model continue. - `"refusal"`: when streaming classifiers intervene to handle potential policy violations - `"model_context_window_exceeded"`: we exceeded the model's context window In non-streaming mode this value is always non-null. In streaming mode, it is null in the `message_start` event and non-null otherwise. """ stop_sequence: Optional[str] = None """Which custom stop sequence was generated, if any. This value will be a non-null string if one of your custom stop sequences was generated. """ type: Literal["message"] """Object type. For Messages, this is always `"message"`. """ usage: Usage """Billing and rate-limit usage. Anthropic's API bills and rate-limits by token counts, as tokens represent the underlying cost to our systems. Under the hood, the API transforms requests into a format suitable for the model. The model's output then goes through a parsing stage before becoming an API response. As a result, the token counts in `usage` will not match one-to-one with the exact visible content of an API request or response. For example, `output_tokens` will be non-zero, even for an empty string response from Claude. Total input tokens in a request is the summation of `input_tokens`, `cache_creation_input_tokens`, and `cache_read_input_tokens`. """ anthropic-sdk-python-0.120.2/src/anthropic/types/message_count_tokens_params.py000066400000000000000000000170621523216435200300300ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Iterable, Optional from typing_extensions import Required, Annotated, TypedDict from .._utils import PropertyInfo from .model_param import ModelParam from .message_param import MessageParam from .text_block_param import TextBlockParam from .tool_choice_param import ToolChoiceParam from .output_config_param import OutputConfigParam from .thinking_config_param import ThinkingConfigParam from .cache_control_ephemeral_param import CacheControlEphemeralParam from .message_count_tokens_tool_param import MessageCountTokensToolParam __all__ = ["MessageCountTokensParams"] class MessageCountTokensParams(TypedDict, total=False): messages: Required[Iterable[MessageParam]] """Input messages. Our models are trained to operate on alternating `user` and `assistant` conversational turns. When creating a new `Message`, you specify the prior conversational turns with the `messages` parameter, and the model then generates the next `Message` in the conversation. Consecutive `user` or `assistant` turns in your request will be combined into a single turn. Each input message must be an object with a `role` and `content`. You can specify a single `user`-role message, or you can include multiple `user` and `assistant` messages. If the final message uses the `assistant` role, the response content will continue immediately from the content in that message. This can be used to constrain part of the model's response. Example with a single `user` message: ```json [{ "role": "user", "content": "Hello, Claude" }] ``` Example with multiple conversational turns: ```json [ { "role": "user", "content": "Hello there." }, { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, { "role": "user", "content": "Can you explain LLMs in plain English?" } ] ``` Example with a partially-filled response from Claude: ```json [ { "role": "user", "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" }, { "role": "assistant", "content": "The best answer is (" } ] ``` Each input message `content` may be either a single `string` or an array of content blocks, where each block has a specific `type`. Using a `string` for `content` is shorthand for an array of one content block of type `"text"`. The following input messages are equivalent: ```json { "role": "user", "content": "Hello, Claude" } ``` ```json { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } ``` See [input examples](https://platform.claude.com/docs/en/build-with-claude/working-with-messages). Note that if you want to include a [system prompt](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role), you can use the top-level `system` parameter — there is no `"system"` role for input messages in the Messages API. There is a limit of 100,000 messages in a single request. """ model: Required[ModelParam] """The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. """ cache_control: Optional[CacheControlEphemeralParam] """ Top-level cache control automatically applies a cache_control marker to the last cacheable block in the request. """ output_config: OutputConfigParam """Configuration options for the model's output, such as the output format.""" system: Union[str, Iterable[TextBlockParam]] """System prompt. A system prompt is a way of providing context and instructions to Claude, such as specifying a particular goal or role. See our [guide to system prompts](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role). """ thinking: ThinkingConfigParam """Configuration for enabling Claude's extended thinking. When enabled, responses include `thinking` content blocks showing Claude's thinking process before the final answer. Requires a minimum budget of 1,024 tokens and counts towards your `max_tokens` limit. See [extended thinking](https://platform.claude.com/docs/en/build-with-claude/extended-thinking) for details. """ tool_choice: ToolChoiceParam """How the model should use the provided tools. The model can use a specific tool, any available tool, decide by itself, or not use tools at all. """ tools: Iterable[MessageCountTokensToolParam] """Definitions of tools that the model may use. If you include `tools` in your API request, the model may return `tool_use` content blocks that represent the model's use of those tools. You can then run those tools using the tool input generated by the model and then optionally return results back to the model using `tool_result` content blocks. There are two types of tools: **client tools** and **server tools**. The behavior described below applies to client tools. For [server tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools), see their individual documentation as each has its own behavior (e.g., the [web search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool)). Each tool definition includes: - `name`: Name of the tool. - `description`: Optional, but strongly-recommended description of the tool. - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the tool `input` shape that the model will produce in `tool_use` output content blocks. For example, if you defined `tools` as: ```json [ { "name": "get_stock_price", "description": "Get the current stock price for a given ticker symbol.", "input_schema": { "type": "object", "properties": { "ticker": { "type": "string", "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." } }, "required": ["ticker"] } } ] ``` And then asked the model "What's the S&P 500 at today?", the model might produce `tool_use` content blocks in the response like this: ```json [ { "type": "tool_use", "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "name": "get_stock_price", "input": { "ticker": "^GSPC" } } ] ``` You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an input, and return the following back to the model in a subsequent `user` message: ```json [ { "type": "tool_result", "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "content": "259.75 USD" } ] ``` Tools can be used for workflows that include running client-side tools and functions, or more generally whenever you want the model to produce a particular JSON structure of output. See our [guide](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview) for more details. """ user_profile_id: Annotated[str, PropertyInfo(alias="anthropic-user-profile-id")] """The user profile ID to attribute this request to. Use when acting on behalf of a party other than your organization. Requires the `user-profiles` beta header. """ anthropic-sdk-python-0.120.2/src/anthropic/types/message_count_tokens_tool_param.py000066400000000000000000000043021523216435200306730ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from typing_extensions import TypeAlias from .tool_param import ToolParam from .tool_bash_20250124_param import ToolBash20250124Param from .memory_tool_20250818_param import MemoryTool20250818Param from .web_fetch_tool_20250910_param import WebFetchTool20250910Param from .web_fetch_tool_20260209_param import WebFetchTool20260209Param from .web_fetch_tool_20260309_param import WebFetchTool20260309Param from .web_fetch_tool_20260318_param import WebFetchTool20260318Param from .web_search_tool_20250305_param import WebSearchTool20250305Param from .web_search_tool_20260209_param import WebSearchTool20260209Param from .web_search_tool_20260318_param import WebSearchTool20260318Param from .tool_text_editor_20250124_param import ToolTextEditor20250124Param from .tool_text_editor_20250429_param import ToolTextEditor20250429Param from .tool_text_editor_20250728_param import ToolTextEditor20250728Param from .code_execution_tool_20250522_param import CodeExecutionTool20250522Param from .code_execution_tool_20250825_param import CodeExecutionTool20250825Param from .code_execution_tool_20260120_param import CodeExecutionTool20260120Param from .code_execution_tool_20260521_param import CodeExecutionTool20260521Param from .tool_search_tool_bm25_20251119_param import ToolSearchToolBm25_20251119Param from .tool_search_tool_regex_20251119_param import ToolSearchToolRegex20251119Param __all__ = ["MessageCountTokensToolParam"] MessageCountTokensToolParam: TypeAlias = Union[ ToolParam, ToolBash20250124Param, CodeExecutionTool20250522Param, CodeExecutionTool20250825Param, CodeExecutionTool20260120Param, CodeExecutionTool20260521Param, MemoryTool20250818Param, ToolTextEditor20250124Param, ToolTextEditor20250429Param, ToolTextEditor20250728Param, WebSearchTool20250305Param, WebFetchTool20250910Param, WebSearchTool20260209Param, WebFetchTool20260209Param, WebFetchTool20260309Param, WebSearchTool20260318Param, WebFetchTool20260318Param, ToolSearchToolBm25_20251119Param, ToolSearchToolRegex20251119Param, ] anthropic-sdk-python-0.120.2/src/anthropic/types/message_create_params.py000066400000000000000000000301201523216435200265460ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Iterable, Optional from typing_extensions import Literal, Required, Annotated, TypeAlias, TypedDict from .._types import SequenceNotStr from .._utils import PropertyInfo from .model_param import ModelParam from .message_param import MessageParam from .metadata_param import MetadataParam from .text_block_param import TextBlockParam from .tool_union_param import ToolUnionParam from .tool_choice_param import ToolChoiceParam from .output_config_param import OutputConfigParam from .thinking_config_param import ThinkingConfigParam from .tool_choice_any_param import ToolChoiceAnyParam from .tool_choice_auto_param import ToolChoiceAutoParam from .tool_choice_tool_param import ToolChoiceToolParam from .cache_control_ephemeral_param import CacheControlEphemeralParam __all__ = [ "MessageCreateParamsBase", "Metadata", "ToolChoice", "ToolChoiceToolChoiceAuto", "ToolChoiceToolChoiceAny", "ToolChoiceToolChoiceTool", "MessageCreateParamsNonStreaming", "MessageCreateParamsStreaming", ] class MessageCreateParamsBase(TypedDict, total=False): max_tokens: Required[int] """The maximum number of tokens to generate before stopping. Note that our models may stop _before_ reaching this maximum. This parameter only specifies the absolute maximum number of tokens to generate. Set to `0` to populate the [prompt cache](https://platform.claude.com/docs/en/build-with-claude/prompt-caching#pre-warming-the-cache) without generating a response. Different models have different maximum values for this parameter. See [models](https://platform.claude.com/docs/en/about-claude/models/overview) for details. """ messages: Required[Iterable[MessageParam]] """Input messages. Our models are trained to operate on alternating `user` and `assistant` conversational turns. When creating a new `Message`, you specify the prior conversational turns with the `messages` parameter, and the model then generates the next `Message` in the conversation. Consecutive `user` or `assistant` turns in your request will be combined into a single turn. Each input message must be an object with a `role` and `content`. You can specify a single `user`-role message, or you can include multiple `user` and `assistant` messages. If the final message uses the `assistant` role, the response content will continue immediately from the content in that message. This can be used to constrain part of the model's response. Example with a single `user` message: ```json [{ "role": "user", "content": "Hello, Claude" }] ``` Example with multiple conversational turns: ```json [ { "role": "user", "content": "Hello there." }, { "role": "assistant", "content": "Hi, I'm Claude. How can I help you?" }, { "role": "user", "content": "Can you explain LLMs in plain English?" } ] ``` Example with a partially-filled response from Claude: ```json [ { "role": "user", "content": "What's the Greek name for Sun? (A) Sol (B) Helios (C) Sun" }, { "role": "assistant", "content": "The best answer is (" } ] ``` Each input message `content` may be either a single `string` or an array of content blocks, where each block has a specific `type`. Using a `string` for `content` is shorthand for an array of one content block of type `"text"`. The following input messages are equivalent: ```json { "role": "user", "content": "Hello, Claude" } ``` ```json { "role": "user", "content": [{ "type": "text", "text": "Hello, Claude" }] } ``` See [input examples](https://platform.claude.com/docs/en/build-with-claude/working-with-messages). Note that if you want to include a [system prompt](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role), you can use the top-level `system` parameter — there is no `"system"` role for input messages in the Messages API. There is a limit of 100,000 messages in a single request. """ model: Required[ModelParam] """The model that will complete your prompt. See [models](https://docs.anthropic.com/en/docs/models-overview) for additional details and options. """ cache_control: Optional[CacheControlEphemeralParam] """ Top-level cache control automatically applies a cache_control marker to the last cacheable block in the request. """ container: Optional[str] """Container identifier for reuse across requests.""" inference_geo: Optional[str] """Specifies the geographic region for inference processing. If not specified, the workspace's `default_inference_geo` is used. """ metadata: MetadataParam """An object describing metadata about the request.""" output_config: OutputConfigParam """Configuration options for the model's output, such as the output format.""" service_tier: Literal["auto", "standard_only"] """ Determines whether to use priority capacity (if available) or standard capacity for this request. Anthropic offers different levels of service for your API requests. See [service-tiers](https://platform.claude.com/docs/en/api/service-tiers) for details. """ stop_sequences: SequenceNotStr[str] """Custom text sequences that will cause the model to stop generating. Our models will normally stop when they have naturally completed their turn, which will result in a response `stop_reason` of `"end_turn"`. If you want the model to stop generating when it encounters custom strings of text, you can use the `stop_sequences` parameter. If the model encounters one of the custom sequences, the response `stop_reason` value will be `"stop_sequence"` and the response `stop_sequence` value will contain the matched stop sequence. """ system: Union[str, Iterable[TextBlockParam]] """System prompt. A system prompt is a way of providing context and instructions to Claude, such as specifying a particular goal or role. See our [guide to system prompts](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#give-claude-a-role). """ temperature: float """Amount of randomness injected into the response. Defaults to `1.0`. Ranges from `0.0` to `1.0`. Use `temperature` closer to `0.0` for analytical / multiple choice, and closer to `1.0` for creative and generative tasks. Note that even with `temperature` of `0.0`, the results will not be fully deterministic. """ thinking: ThinkingConfigParam """Configuration for enabling Claude's extended thinking. When enabled, responses include `thinking` content blocks showing Claude's thinking process before the final answer. Requires a minimum budget of 1,024 tokens and counts towards your `max_tokens` limit. See [extended thinking](https://platform.claude.com/docs/en/build-with-claude/extended-thinking) for details. """ tool_choice: ToolChoiceParam """How the model should use the provided tools. The model can use a specific tool, any available tool, decide by itself, or not use tools at all. """ tools: Iterable[ToolUnionParam] """Definitions of tools that the model may use. If you include `tools` in your API request, the model may return `tool_use` content blocks that represent the model's use of those tools. You can then run those tools using the tool input generated by the model and then optionally return results back to the model using `tool_result` content blocks. There are two types of tools: **client tools** and **server tools**. The behavior described below applies to client tools. For [server tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools), see their individual documentation as each has its own behavior (e.g., the [web search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool)). Each tool definition includes: - `name`: Name of the tool. - `description`: Optional, but strongly-recommended description of the tool. - `input_schema`: [JSON schema](https://json-schema.org/draft/2020-12) for the tool `input` shape that the model will produce in `tool_use` output content blocks. For example, if you defined `tools` as: ```json [ { "name": "get_stock_price", "description": "Get the current stock price for a given ticker symbol.", "input_schema": { "type": "object", "properties": { "ticker": { "type": "string", "description": "The stock ticker symbol, e.g. AAPL for Apple Inc." } }, "required": ["ticker"] } } ] ``` And then asked the model "What's the S&P 500 at today?", the model might produce `tool_use` content blocks in the response like this: ```json [ { "type": "tool_use", "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "name": "get_stock_price", "input": { "ticker": "^GSPC" } } ] ``` You might then run your `get_stock_price` tool with `{"ticker": "^GSPC"}` as an input, and return the following back to the model in a subsequent `user` message: ```json [ { "type": "tool_result", "tool_use_id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "content": "259.75 USD" } ] ``` Tools can be used for workflows that include running client-side tools and functions, or more generally whenever you want the model to produce a particular JSON structure of output. See our [guide](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview) for more details. """ top_k: int """Only sample from the top K options for each subsequent token. Used to remove "long tail" low probability responses. [Learn more technical details here](https://towardsdatascience.com/how-to-sample-from-language-models-682bceb97277). Recommended for advanced use cases only. """ top_p: float """Use nucleus sampling. In nucleus sampling, we compute the cumulative distribution over all the options for each subsequent token in decreasing probability order and cut it off once it reaches a particular probability specified by `top_p`. Recommended for advanced use cases only. """ user_profile_id: Annotated[str, PropertyInfo(alias="anthropic-user-profile-id")] """The user profile ID to attribute this request to. Use when acting on behalf of a party other than your organization. Requires the `user-profiles` beta header. """ Metadata: TypeAlias = MetadataParam """This is deprecated, `MetadataParam` should be used instead""" ToolChoice: TypeAlias = ToolChoiceParam """This is deprecated, `ToolChoiceParam` should be used instead""" ToolChoiceToolChoiceAuto: TypeAlias = ToolChoiceAutoParam """This is deprecated, `ToolChoiceAutoParam` should be used instead""" ToolChoiceToolChoiceAny: TypeAlias = ToolChoiceAnyParam """This is deprecated, `ToolChoiceAnyParam` should be used instead""" ToolChoiceToolChoiceTool: TypeAlias = ToolChoiceToolParam """This is deprecated, `ToolChoiceToolParam` should be used instead""" class MessageCreateParamsNonStreaming(MessageCreateParamsBase, total=False): stream: Literal[False] """Whether to incrementally stream the response using server-sent events. See [streaming](https://platform.claude.com/docs/en/build-with-claude/streaming) for details. """ class MessageCreateParamsStreaming(MessageCreateParamsBase): stream: Required[Literal[True]] """Whether to incrementally stream the response using server-sent events. See [streaming](https://platform.claude.com/docs/en/build-with-claude/streaming) for details. """ MessageCreateParams = Union[MessageCreateParamsNonStreaming, MessageCreateParamsStreaming] anthropic-sdk-python-0.120.2/src/anthropic/types/message_delta_event.py000066400000000000000000000004271523216435200262410ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .raw_message_delta_event import RawMessageDeltaEvent __all__ = ["MessageDeltaEvent"] MessageDeltaEvent = RawMessageDeltaEvent """The RawMessageDeltaEvent type should be used instead""" anthropic-sdk-python-0.120.2/src/anthropic/types/message_delta_usage.py000066400000000000000000000024211523216435200262200ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from .._models import BaseModel from .server_tool_usage import ServerToolUsage from .output_tokens_details import OutputTokensDetails __all__ = ["MessageDeltaUsage"] class MessageDeltaUsage(BaseModel): cache_creation_input_tokens: Optional[int] = None """The cumulative number of input tokens used to create the cache entry.""" cache_read_input_tokens: Optional[int] = None """The cumulative number of input tokens read from the cache.""" input_tokens: Optional[int] = None """The cumulative number of input tokens which were used.""" output_tokens: int """The cumulative number of output tokens which were used.""" output_tokens_details: Optional[OutputTokensDetails] = None """Breakdown of output tokens by category. `output_tokens` remains the inclusive, authoritative total used for billing. This object provides a read-only decomposition for observability — for example, how many of the billed output tokens were spent on internal reasoning that may have been summarized before being returned to you. """ server_tool_use: Optional[ServerToolUsage] = None """The number of server tool requests.""" anthropic-sdk-python-0.120.2/src/anthropic/types/message_param.py000066400000000000000000000047731523216435200250570ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Iterable from typing_extensions import Literal, Required, TypedDict from .content_block import ContentBlock from .text_block_param import TextBlockParam from .image_block_param import ImageBlockParam from .document_block_param import DocumentBlockParam from .thinking_block_param import ThinkingBlockParam from .tool_use_block_param import ToolUseBlockParam from .tool_result_block_param import ToolResultBlockParam from .search_result_block_param import SearchResultBlockParam from .server_tool_use_block_param import ServerToolUseBlockParam from .container_upload_block_param import ContainerUploadBlockParam from .redacted_thinking_block_param import RedactedThinkingBlockParam from .web_fetch_tool_result_block_param import WebFetchToolResultBlockParam from .web_search_tool_result_block_param import WebSearchToolResultBlockParam from .mid_conversation_system_block_param import MidConversationSystemBlockParam from .tool_search_tool_result_block_param import ToolSearchToolResultBlockParam from .code_execution_tool_result_block_param import CodeExecutionToolResultBlockParam from .bash_code_execution_tool_result_block_param import BashCodeExecutionToolResultBlockParam from .text_editor_code_execution_tool_result_block_param import TextEditorCodeExecutionToolResultBlockParam __all__ = ["MessageParam"] class MessageParam(TypedDict, total=False): content: Required[ Union[ str, Iterable[ Union[ TextBlockParam, ImageBlockParam, DocumentBlockParam, SearchResultBlockParam, ThinkingBlockParam, RedactedThinkingBlockParam, ToolUseBlockParam, ToolResultBlockParam, ServerToolUseBlockParam, WebSearchToolResultBlockParam, WebFetchToolResultBlockParam, CodeExecutionToolResultBlockParam, BashCodeExecutionToolResultBlockParam, TextEditorCodeExecutionToolResultBlockParam, ToolSearchToolResultBlockParam, ContainerUploadBlockParam, MidConversationSystemBlockParam, ContentBlock, ] ], ] ] role: Required[Literal["user", "assistant", "system"]] anthropic-sdk-python-0.120.2/src/anthropic/types/message_start_event.py000066400000000000000000000004271523216435200263050ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .raw_message_start_event import RawMessageStartEvent __all__ = ["MessageStartEvent"] MessageStartEvent = RawMessageStartEvent """The RawMessageStartEvent type should be used instead""" anthropic-sdk-python-0.120.2/src/anthropic/types/message_stop_event.py000066400000000000000000000004211523216435200261270ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .raw_message_stop_event import RawMessageStopEvent __all__ = ["MessageStopEvent"] MessageStopEvent = RawMessageStopEvent """The RawMessageStopEvent type should be used instead""" anthropic-sdk-python-0.120.2/src/anthropic/types/message_stream_event.py000066400000000000000000000004351523216435200264420ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .raw_message_stream_event import RawMessageStreamEvent __all__ = ["MessageStreamEvent"] MessageStreamEvent = RawMessageStreamEvent """The RawMessageStreamEvent type should be used instead""" anthropic-sdk-python-0.120.2/src/anthropic/types/message_tokens_count.py000066400000000000000000000005111523216435200264540ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .._models import BaseModel __all__ = ["MessageTokensCount"] class MessageTokensCount(BaseModel): input_tokens: int """ The total number of tokens across the provided list of messages, system prompt, and tools. """ anthropic-sdk-python-0.120.2/src/anthropic/types/messages/000077500000000000000000000000001523216435200234755ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/messages/__init__.py000066400000000000000000000020641523216435200256100ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from .message_batch import MessageBatch as MessageBatch from .batch_list_params import BatchListParams as BatchListParams from .batch_create_params import BatchCreateParams as BatchCreateParams from .message_batch_result import MessageBatchResult as MessageBatchResult from .deleted_message_batch import DeletedMessageBatch as DeletedMessageBatch from .message_batch_errored_result import MessageBatchErroredResult as MessageBatchErroredResult from .message_batch_expired_result import MessageBatchExpiredResult as MessageBatchExpiredResult from .message_batch_request_counts import MessageBatchRequestCounts as MessageBatchRequestCounts from .message_batch_canceled_result import MessageBatchCanceledResult as MessageBatchCanceledResult from .message_batch_succeeded_result import MessageBatchSucceededResult as MessageBatchSucceededResult from .message_batch_individual_response import MessageBatchIndividualResponse as MessageBatchIndividualResponse anthropic-sdk-python-0.120.2/src/anthropic/types/messages/batch_create_params.py000066400000000000000000000030511523216435200300150ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Iterable from typing_extensions import Required, Annotated, TypedDict from ..._utils import PropertyInfo from ..message_create_params import MessageCreateParamsNonStreaming __all__ = ["BatchCreateParams", "Request"] class BatchCreateParams(TypedDict, total=False): requests: Required[Iterable[Request]] """List of requests for prompt completion. Each is an individual request to create a Message. """ user_profile_id: Annotated[str, PropertyInfo(alias="anthropic-user-profile-id")] """The user profile ID to attribute the requests in this batch to. Use when acting on behalf of a party other than your organization. Requires the `user-profiles` beta header. Applies to every request in the batch; an individual request whose `user_profile_id` body field conflicts with this header is errored. """ class Request(TypedDict, total=False): custom_id: Required[str] """Developer-provided ID created for each request in a Message Batch. Useful for matching results to requests, as results may be given out of request order. Must be unique for each request within the Message Batch. """ params: Required[MessageCreateParamsNonStreaming] """Messages API creation parameters for the individual request. See the [Messages API reference](https://platform.claude.com/docs/en/api/messages) for full documentation on available parameters. """ anthropic-sdk-python-0.120.2/src/anthropic/types/messages/batch_list_params.py000066400000000000000000000012631523216435200275300ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import TypedDict __all__ = ["BatchListParams"] class BatchListParams(TypedDict, total=False): after_id: str """ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately after this object. """ before_id: str """ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately before this object. """ limit: int """Number of items to return per page. Defaults to `20`. Ranges from `1` to `1000`. """ anthropic-sdk-python-0.120.2/src/anthropic/types/messages/deleted_message_batch.py000066400000000000000000000006551523216435200303300ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["DeletedMessageBatch"] class DeletedMessageBatch(BaseModel): id: str """ID of the Message Batch.""" type: Literal["message_batch_deleted"] """Deleted object type. For Message Batches, this is always `"message_batch_deleted"`. """ anthropic-sdk-python-0.120.2/src/anthropic/types/messages/message_batch.py000066400000000000000000000045571523216435200266470ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from datetime import datetime from typing_extensions import Literal from ..._models import BaseModel from .message_batch_request_counts import MessageBatchRequestCounts __all__ = ["MessageBatch"] class MessageBatch(BaseModel): id: str """Unique object identifier. The format and length of IDs may change over time. """ archived_at: Optional[datetime] = None """ RFC 3339 datetime string representing the time at which the Message Batch was archived and its results became unavailable. """ cancel_initiated_at: Optional[datetime] = None """ RFC 3339 datetime string representing the time at which cancellation was initiated for the Message Batch. Specified only if cancellation was initiated. """ created_at: datetime """ RFC 3339 datetime string representing the time at which the Message Batch was created. """ ended_at: Optional[datetime] = None """ RFC 3339 datetime string representing the time at which processing for the Message Batch ended. Specified only once processing ends. Processing ends when every request in a Message Batch has either succeeded, errored, canceled, or expired. """ expires_at: datetime """ RFC 3339 datetime string representing the time at which the Message Batch will expire and end processing, which is 24 hours after creation. """ processing_status: Literal["in_progress", "canceling", "ended"] """Processing status of the Message Batch.""" request_counts: MessageBatchRequestCounts """Tallies requests within the Message Batch, categorized by their status. Requests start as `processing` and move to one of the other statuses only once processing of the entire batch ends. The sum of all values always matches the total number of requests in the batch. """ results_url: Optional[str] = None """URL to a `.jsonl` file containing the results of the Message Batch requests. Specified only once processing ends. Results in the file are not guaranteed to be in the same order as requests. Use the `custom_id` field to match results to requests. """ type: Literal["message_batch"] """Object type. For Message Batches, this is always `"message_batch"`. """ anthropic-sdk-python-0.120.2/src/anthropic/types/messages/message_batch_canceled_result.py000066400000000000000000000004261523216435200320520ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["MessageBatchCanceledResult"] class MessageBatchCanceledResult(BaseModel): type: Literal["canceled"] anthropic-sdk-python-0.120.2/src/anthropic/types/messages/message_batch_errored_result.py000066400000000000000000000005371523216435200317610ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel from ..shared.error_response import ErrorResponse __all__ = ["MessageBatchErroredResult"] class MessageBatchErroredResult(BaseModel): error: ErrorResponse type: Literal["errored"] anthropic-sdk-python-0.120.2/src/anthropic/types/messages/message_batch_expired_result.py000066400000000000000000000004231523216435200317510ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["MessageBatchExpiredResult"] class MessageBatchExpiredResult(BaseModel): type: Literal["expired"] anthropic-sdk-python-0.120.2/src/anthropic/types/messages/message_batch_individual_response.py000066400000000000000000000016371523216435200327710ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from ..._models import BaseModel from .message_batch_result import MessageBatchResult __all__ = ["MessageBatchIndividualResponse"] class MessageBatchIndividualResponse(BaseModel): """ This is a single line in the response `.jsonl` file and does not represent the response as a whole. """ custom_id: str """Developer-provided ID created for each request in a Message Batch. Useful for matching results to requests, as results may be given out of request order. Must be unique for each request within the Message Batch. """ result: MessageBatchResult """Processing result for this request. Contains a Message output if processing was successful, an error response if processing failed, or the reason why processing was not attempted, such as cancellation or expiration. """ anthropic-sdk-python-0.120.2/src/anthropic/types/messages/message_batch_request_counts.py000066400000000000000000000017421523216435200320030ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from ..._models import BaseModel __all__ = ["MessageBatchRequestCounts"] class MessageBatchRequestCounts(BaseModel): canceled: int """Number of requests in the Message Batch that have been canceled. This is zero until processing of the entire Message Batch has ended. """ errored: int """Number of requests in the Message Batch that encountered an error. This is zero until processing of the entire Message Batch has ended. """ expired: int """Number of requests in the Message Batch that have expired. This is zero until processing of the entire Message Batch has ended. """ processing: int """Number of requests in the Message Batch that are processing.""" succeeded: int """Number of requests in the Message Batch that have completed successfully. This is zero until processing of the entire Message Batch has ended. """ anthropic-sdk-python-0.120.2/src/anthropic/types/messages/message_batch_result.py000066400000000000000000000013351523216435200302340ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from ..._utils import PropertyInfo from .message_batch_errored_result import MessageBatchErroredResult from .message_batch_expired_result import MessageBatchExpiredResult from .message_batch_canceled_result import MessageBatchCanceledResult from .message_batch_succeeded_result import MessageBatchSucceededResult __all__ = ["MessageBatchResult"] MessageBatchResult: TypeAlias = Annotated[ Union[ MessageBatchSucceededResult, MessageBatchErroredResult, MessageBatchCanceledResult, MessageBatchExpiredResult ], PropertyInfo(discriminator="type"), ] anthropic-sdk-python-0.120.2/src/anthropic/types/messages/message_batch_succeeded_result.py000066400000000000000000000005151523216435200322370ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..message import Message from ..._models import BaseModel __all__ = ["MessageBatchSucceededResult"] class MessageBatchSucceededResult(BaseModel): message: Message type: Literal["succeeded"] anthropic-sdk-python-0.120.2/src/anthropic/types/metadata_param.py000066400000000000000000000011221523216435200251740ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import TypedDict __all__ = ["MetadataParam"] class MetadataParam(TypedDict, total=False): user_id: Optional[str] """An external identifier for the user who is associated with the request. This should be a uuid, hash value, or other opaque identifier. Anthropic may use this id to help detect abuse. Do not include any identifying information such as name, email address, or phone number. """ anthropic-sdk-python-0.120.2/src/anthropic/types/mid_conversation_system_block_param.py000066400000000000000000000016571523216435200315520ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Iterable, Optional from typing_extensions import Literal, Required, TypedDict from .text_block_param import TextBlockParam from .cache_control_ephemeral_param import CacheControlEphemeralParam __all__ = ["MidConversationSystemBlockParam"] class MidConversationSystemBlockParam(TypedDict, total=False): """System instructions that appear mid-conversation. Use this block to provide or update system-level instructions at a specific point in the conversation, rather than only via the top-level `system` parameter. """ content: Required[Iterable[TextBlockParam]] """System instruction text blocks.""" type: Required[Literal["mid_conv_system"]] cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" anthropic-sdk-python-0.120.2/src/anthropic/types/model.py000066400000000000000000000013521523216435200233410ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Literal, TypeAlias __all__ = ["Model"] Model: TypeAlias = Union[ Literal[ "claude-sonnet-5", "claude-fable-5", "claude-mythos-5", "claude-opus-5", "claude-opus-4-8", "claude-opus-4-7", "claude-mythos-preview", "claude-opus-4-6", "claude-sonnet-4-6", "claude-haiku-4-5", "claude-haiku-4-5-20251001", "claude-opus-4-5", "claude-opus-4-5-20251101", "claude-sonnet-4-5", "claude-sonnet-4-5-20250929", "claude-opus-4-1", "claude-opus-4-1-20250805", ], str, ] anthropic-sdk-python-0.120.2/src/anthropic/types/model_capabilities.py000066400000000000000000000025071523216435200260550ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .._models import BaseModel from .effort_capability import EffortCapability from .capability_support import CapabilitySupport from .thinking_capability import ThinkingCapability from .context_management_capability import ContextManagementCapability __all__ = ["ModelCapabilities"] class ModelCapabilities(BaseModel): """Model capability information.""" batch: CapabilitySupport """Whether the model supports the Batch API.""" citations: CapabilitySupport """Whether the model supports citation generation.""" code_execution: CapabilitySupport """Whether the model supports code execution tools.""" context_management: ContextManagementCapability """Context management support and available strategies.""" effort: EffortCapability """Effort (reasoning_effort) support and available levels.""" image_input: CapabilitySupport """Whether the model accepts image content blocks.""" pdf_input: CapabilitySupport """Whether the model accepts PDF content blocks.""" structured_outputs: CapabilitySupport """Whether the model supports structured output / JSON mode / strict tool schemas.""" thinking: ThinkingCapability """Thinking capability and supported type configurations.""" anthropic-sdk-python-0.120.2/src/anthropic/types/model_info.py000066400000000000000000000020301523216435200243460ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from datetime import datetime from typing_extensions import Literal from .._models import BaseModel from .model_capabilities import ModelCapabilities __all__ = ["ModelInfo"] class ModelInfo(BaseModel): id: str """Unique model identifier.""" capabilities: Optional[ModelCapabilities] = None """Model capability information.""" created_at: datetime """RFC 3339 datetime string representing the time at which the model was released. May be set to an epoch value if the release date is unknown. """ display_name: str """A human-readable name for the model.""" max_input_tokens: Optional[int] = None """Maximum input context window size in tokens for this model.""" max_tokens: Optional[int] = None """Maximum value for the `max_tokens` parameter when using this model.""" type: Literal["model"] """Object type. For Models, this is always `"model"`. """ anthropic-sdk-python-0.120.2/src/anthropic/types/model_list_params.py000066400000000000000000000017161523216435200257430ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List from typing_extensions import Annotated, TypedDict from .._utils import PropertyInfo from .anthropic_beta_param import AnthropicBetaParam __all__ = ["ModelListParams"] class ModelListParams(TypedDict, total=False): after_id: str """ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately after this object. """ before_id: str """ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately before this object. """ limit: int """Number of items to return per page. Defaults to `20`. Ranges from `1` to `1000`. """ betas: Annotated[List[AnthropicBetaParam], PropertyInfo(alias="anthropic-beta")] """Optional header to specify the beta version(s) you want to use.""" anthropic-sdk-python-0.120.2/src/anthropic/types/model_param.py000066400000000000000000000014301523216435200245160ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from typing_extensions import Literal, TypeAlias __all__ = ["ModelParam"] ModelParam: TypeAlias = Union[ Literal[ "claude-sonnet-5", "claude-fable-5", "claude-mythos-5", "claude-opus-5", "claude-opus-4-8", "claude-opus-4-7", "claude-mythos-preview", "claude-opus-4-6", "claude-sonnet-4-6", "claude-haiku-4-5", "claude-haiku-4-5-20251001", "claude-opus-4-5", "claude-opus-4-5-20251101", "claude-sonnet-4-5", "claude-sonnet-4-5-20250929", "claude-opus-4-1", "claude-opus-4-1-20250805", ], str, ] anthropic-sdk-python-0.120.2/src/anthropic/types/output_config_param.py000066400000000000000000000012471523216435200263110ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, TypedDict from .json_output_format_param import JSONOutputFormatParam __all__ = ["OutputConfigParam"] class OutputConfigParam(TypedDict, total=False): effort: Optional[Literal["low", "medium", "high", "xhigh", "max"]] """All possible effort levels.""" format: Optional[JSONOutputFormatParam] """A schema to specify Claude's output format in responses. See [structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs) """ anthropic-sdk-python-0.120.2/src/anthropic/types/output_tokens_details.py000066400000000000000000000013511523216435200266700ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .._models import BaseModel __all__ = ["OutputTokensDetails"] class OutputTokensDetails(BaseModel): thinking_tokens: int """ Number of output tokens the model generated as internal reasoning, including the thinking-block delimiter tokens. Reflects the raw reasoning the model produced, not the (possibly shorter) summarized thinking text returned in the response body. Computed by re-tokenizing the raw reasoning text, so it may differ from the model's exact generation count by a small number of tokens. Always ≤ `output_tokens`; `output_tokens - thinking_tokens` approximates the non-reasoning output. """ anthropic-sdk-python-0.120.2/src/anthropic/types/parsed_message.py000066400000000000000000000044011523216435200252210ustar00rootroot00000000000000from __future__ import annotations from typing import TYPE_CHECKING, List, Union, Generic, Optional from typing_extensions import TypeVar, Annotated, TypeAlias from .._utils import PropertyInfo from .message import Message from .text_block import TextBlock from .thinking_block import ThinkingBlock from .tool_use_block import ToolUseBlock from .server_tool_use_block import ServerToolUseBlock from .container_upload_block import ContainerUploadBlock from .redacted_thinking_block import RedactedThinkingBlock from .web_fetch_tool_result_block import WebFetchToolResultBlock from .web_search_tool_result_block import WebSearchToolResultBlock from .tool_search_tool_result_block import ToolSearchToolResultBlock from .code_execution_tool_result_block import CodeExecutionToolResultBlock from .bash_code_execution_tool_result_block import BashCodeExecutionToolResultBlock from .text_editor_code_execution_tool_result_block import TextEditorCodeExecutionToolResultBlock ResponseFormatT = TypeVar("ResponseFormatT", default=None) __all__ = [ "ParsedTextBlock", "ParsedContentBlock", "ParsedMessage", ] class ParsedTextBlock(TextBlock, Generic[ResponseFormatT]): parsed_output: Optional[ResponseFormatT] = None __api_exclude__ = {"parsed_output"} # Note that generic unions are not valid for pydantic at runtime ParsedContentBlock: TypeAlias = Annotated[ Union[ ParsedTextBlock[ResponseFormatT], ThinkingBlock, RedactedThinkingBlock, ToolUseBlock, ServerToolUseBlock, WebSearchToolResultBlock, WebFetchToolResultBlock, CodeExecutionToolResultBlock, BashCodeExecutionToolResultBlock, TextEditorCodeExecutionToolResultBlock, ToolSearchToolResultBlock, ContainerUploadBlock, ], PropertyInfo(discriminator="type"), ] class ParsedMessage(Message, Generic[ResponseFormatT]): if TYPE_CHECKING: content: List[ParsedContentBlock[ResponseFormatT]] # type: ignore[assignment] else: content: List[ParsedContentBlock] @property def parsed_output(self) -> Optional[ResponseFormatT]: for content in self.content: if content.type == "text" and content.parsed_output: return content.parsed_output return None anthropic-sdk-python-0.120.2/src/anthropic/types/plain_text_source.py000066400000000000000000000004611523216435200257700ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel __all__ = ["PlainTextSource"] class PlainTextSource(BaseModel): data: str media_type: Literal["text/plain"] type: Literal["text"] anthropic-sdk-python-0.120.2/src/anthropic/types/plain_text_source_param.py000066400000000000000000000005761523216435200271570ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["PlainTextSourceParam"] class PlainTextSourceParam(TypedDict, total=False): data: Required[str] media_type: Required[Literal["text/plain"]] type: Required[Literal["text"]] anthropic-sdk-python-0.120.2/src/anthropic/types/raw_content_block_delta.py000066400000000000000000000011431523216435200271050ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from .._utils import PropertyInfo from .text_delta import TextDelta from .thinking_delta import ThinkingDelta from .citations_delta import CitationsDelta from .signature_delta import SignatureDelta from .input_json_delta import InputJSONDelta __all__ = ["RawContentBlockDelta"] RawContentBlockDelta: TypeAlias = Annotated[ Union[TextDelta, InputJSONDelta, CitationsDelta, ThinkingDelta, SignatureDelta], PropertyInfo(discriminator="type") ] anthropic-sdk-python-0.120.2/src/anthropic/types/raw_content_block_delta_event.py000066400000000000000000000006111523216435200303050ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel from .raw_content_block_delta import RawContentBlockDelta __all__ = ["RawContentBlockDeltaEvent"] class RawContentBlockDeltaEvent(BaseModel): delta: RawContentBlockDelta index: int type: Literal["content_block_delta"] anthropic-sdk-python-0.120.2/src/anthropic/types/raw_content_block_start_event.py000066400000000000000000000032701523216435200303550ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Literal, Annotated, TypeAlias from .._utils import PropertyInfo from .._models import BaseModel from .text_block import TextBlock from .thinking_block import ThinkingBlock from .tool_use_block import ToolUseBlock from .server_tool_use_block import ServerToolUseBlock from .container_upload_block import ContainerUploadBlock from .redacted_thinking_block import RedactedThinkingBlock from .web_fetch_tool_result_block import WebFetchToolResultBlock from .web_search_tool_result_block import WebSearchToolResultBlock from .tool_search_tool_result_block import ToolSearchToolResultBlock from .code_execution_tool_result_block import CodeExecutionToolResultBlock from .bash_code_execution_tool_result_block import BashCodeExecutionToolResultBlock from .text_editor_code_execution_tool_result_block import TextEditorCodeExecutionToolResultBlock __all__ = ["RawContentBlockStartEvent", "ContentBlock"] ContentBlock: TypeAlias = Annotated[ Union[ TextBlock, ThinkingBlock, RedactedThinkingBlock, ToolUseBlock, ServerToolUseBlock, WebSearchToolResultBlock, WebFetchToolResultBlock, CodeExecutionToolResultBlock, BashCodeExecutionToolResultBlock, TextEditorCodeExecutionToolResultBlock, ToolSearchToolResultBlock, ContainerUploadBlock, ], PropertyInfo(discriminator="type"), ] class RawContentBlockStartEvent(BaseModel): content_block: ContentBlock """Response model for a file uploaded to the container.""" index: int type: Literal["content_block_start"] anthropic-sdk-python-0.120.2/src/anthropic/types/raw_content_block_stop_event.py000066400000000000000000000004531523216435200302050ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel __all__ = ["RawContentBlockStopEvent"] class RawContentBlockStopEvent(BaseModel): index: int type: Literal["content_block_stop"] anthropic-sdk-python-0.120.2/src/anthropic/types/raw_message_delta_event.py000066400000000000000000000031171523216435200271110ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from .._models import BaseModel from .container import Container from .stop_reason import StopReason from .message_delta_usage import MessageDeltaUsage from .refusal_stop_details import RefusalStopDetails __all__ = ["RawMessageDeltaEvent", "Delta"] class Delta(BaseModel): container: Optional[Container] = None """ Information about the container used in the request (for the code execution tool) """ stop_details: Optional[RefusalStopDetails] = None """Structured information about a refusal.""" stop_reason: Optional[StopReason] = None stop_sequence: Optional[str] = None class RawMessageDeltaEvent(BaseModel): delta: Delta type: Literal["message_delta"] usage: MessageDeltaUsage """Billing and rate-limit usage. Anthropic's API bills and rate-limits by token counts, as tokens represent the underlying cost to our systems. Under the hood, the API transforms requests into a format suitable for the model. The model's output then goes through a parsing stage before becoming an API response. As a result, the token counts in `usage` will not match one-to-one with the exact visible content of an API request or response. For example, `output_tokens` will be non-zero, even for an empty string response from Claude. Total input tokens in a request is the summation of `input_tokens`, `cache_creation_input_tokens`, and `cache_read_input_tokens`. """ anthropic-sdk-python-0.120.2/src/anthropic/types/raw_message_start_event.py000066400000000000000000000005011523216435200271470ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .message import Message from .._models import BaseModel __all__ = ["RawMessageStartEvent"] class RawMessageStartEvent(BaseModel): message: Message type: Literal["message_start"] anthropic-sdk-python-0.120.2/src/anthropic/types/raw_message_stop_event.py000066400000000000000000000004131523216435200270010ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel __all__ = ["RawMessageStopEvent"] class RawMessageStopEvent(BaseModel): type: Literal["message_stop"] anthropic-sdk-python-0.120.2/src/anthropic/types/raw_message_stream_event.py000066400000000000000000000016201523216435200273100ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from .._utils import PropertyInfo from .raw_message_stop_event import RawMessageStopEvent from .raw_message_delta_event import RawMessageDeltaEvent from .raw_message_start_event import RawMessageStartEvent from .raw_content_block_stop_event import RawContentBlockStopEvent from .raw_content_block_delta_event import RawContentBlockDeltaEvent from .raw_content_block_start_event import RawContentBlockStartEvent __all__ = ["RawMessageStreamEvent"] RawMessageStreamEvent: TypeAlias = Annotated[ Union[ RawMessageStartEvent, RawMessageDeltaEvent, RawMessageStopEvent, RawContentBlockStartEvent, RawContentBlockDeltaEvent, RawContentBlockStopEvent, ], PropertyInfo(discriminator="type"), ] anthropic-sdk-python-0.120.2/src/anthropic/types/redacted_thinking_block.py000066400000000000000000000004431523216435200270610ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel __all__ = ["RedactedThinkingBlock"] class RedactedThinkingBlock(BaseModel): data: str type: Literal["redacted_thinking"] anthropic-sdk-python-0.120.2/src/anthropic/types/redacted_thinking_block_param.py000066400000000000000000000005461523216435200302450ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["RedactedThinkingBlockParam"] class RedactedThinkingBlockParam(TypedDict, total=False): data: Required[str] type: Required[Literal["redacted_thinking"]] anthropic-sdk-python-0.120.2/src/anthropic/types/refusal_stop_details.py000066400000000000000000000033131523216435200264530ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from .._models import BaseModel __all__ = ["RefusalStopDetails"] class RefusalStopDetails(BaseModel): """Structured information about a refusal.""" category: Optional[Literal["cyber", "bio", "frontier_llm", "reasoning_extraction", "general_harms"]] = None """The policy category that triggered a refusal. - `cyber` - The request could enable cyber harm, such as malware or exploit development. Benign cybersecurity work can also trigger this category. - `bio` - The request could enable biological harm, such as dangerous lab methods. Beneficial life sciences work can also trigger this category. - `frontier_llm` - The request could assist the development of competing AI models, which is restricted under [Anthropic's commercial terms](https://www.anthropic.com/legal/commercial-terms). Benign machine learning work can also trigger this category. - `reasoning_extraction` - The request asks the model to reproduce its internal reasoning in the response text. To get reasoning in a structured form instead, use [adaptive thinking](https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking). - `general_harms` - The request could be related to an area that was determined as harmful. Benign work might sometimes trigger this category. """ explanation: Optional[str] = None """Human-readable explanation of the refusal. This text is not guaranteed to be stable. `null` when no explanation is available for the category. """ type: Literal["refusal"] anthropic-sdk-python-0.120.2/src/anthropic/types/search_result_block_param.py000066400000000000000000000014331523216435200274360ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Iterable, Optional from typing_extensions import Literal, Required, TypedDict from .text_block_param import TextBlockParam from .citations_config_param import CitationsConfigParam from .cache_control_ephemeral_param import CacheControlEphemeralParam __all__ = ["SearchResultBlockParam"] class SearchResultBlockParam(TypedDict, total=False): content: Required[Iterable[TextBlockParam]] source: Required[str] title: Required[str] type: Required[Literal["search_result"]] cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" citations: CitationsConfigParam anthropic-sdk-python-0.120.2/src/anthropic/types/server_tool_caller.py000066400000000000000000000005361523216435200261310ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel __all__ = ["ServerToolCaller"] class ServerToolCaller(BaseModel): """Tool invocation generated by a server-side tool.""" tool_id: str type: Literal["code_execution_20250825"] anthropic-sdk-python-0.120.2/src/anthropic/types/server_tool_caller_20260120.py000066400000000000000000000004621523216435200271030ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel __all__ = ["ServerToolCaller20260120"] class ServerToolCaller20260120(BaseModel): tool_id: str type: Literal["code_execution_20260120"] anthropic-sdk-python-0.120.2/src/anthropic/types/server_tool_caller_20260120_param.py000066400000000000000000000005651523216435200302670ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["ServerToolCaller20260120Param"] class ServerToolCaller20260120Param(TypedDict, total=False): tool_id: Required[str] type: Required[Literal["code_execution_20260120"]] anthropic-sdk-python-0.120.2/src/anthropic/types/server_tool_caller_param.py000066400000000000000000000006411523216435200273060ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["ServerToolCallerParam"] class ServerToolCallerParam(TypedDict, total=False): """Tool invocation generated by a server-side tool.""" tool_id: Required[str] type: Required[Literal["code_execution_20250825"]] anthropic-sdk-python-0.120.2/src/anthropic/types/server_tool_usage.py000066400000000000000000000005271523216435200257730ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .._models import BaseModel __all__ = ["ServerToolUsage"] class ServerToolUsage(BaseModel): web_fetch_requests: int """The number of web fetch tool requests.""" web_search_requests: int """The number of web search tool requests.""" anthropic-sdk-python-0.120.2/src/anthropic/types/server_tool_use_block.py000066400000000000000000000020061523216435200266270ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Dict, Union, Optional from typing_extensions import Literal, Annotated, TypeAlias from .._utils import PropertyInfo from .._models import BaseModel from .direct_caller import DirectCaller from .server_tool_caller import ServerToolCaller from .server_tool_caller_20260120 import ServerToolCaller20260120 __all__ = ["ServerToolUseBlock", "Caller"] Caller: TypeAlias = Annotated[ Union[DirectCaller, ServerToolCaller, ServerToolCaller20260120], PropertyInfo(discriminator="type") ] class ServerToolUseBlock(BaseModel): id: str caller: Optional[Caller] = None """Tool invocation directly from the model.""" input: Dict[str, object] name: Literal[ "web_search", "web_fetch", "code_execution", "bash_code_execution", "text_editor_code_execution", "tool_search_tool_regex", "tool_search_tool_bm25", ] type: Literal["server_tool_use"] anthropic-sdk-python-0.120.2/src/anthropic/types/server_tool_use_block_param.py000066400000000000000000000024131523216435200300110ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .direct_caller_param import DirectCallerParam from .server_tool_caller_param import ServerToolCallerParam from .cache_control_ephemeral_param import CacheControlEphemeralParam from .server_tool_caller_20260120_param import ServerToolCaller20260120Param __all__ = ["ServerToolUseBlockParam", "Caller"] Caller: TypeAlias = Union[DirectCallerParam, ServerToolCallerParam, ServerToolCaller20260120Param] class ServerToolUseBlockParam(TypedDict, total=False): id: Required[str] input: Required[Dict[str, object]] name: Required[ Literal[ "web_search", "web_fetch", "code_execution", "bash_code_execution", "text_editor_code_execution", "tool_search_tool_regex", "tool_search_tool_bm25", ] ] type: Required[Literal["server_tool_use"]] cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" caller: Caller """Tool invocation directly from the model.""" anthropic-sdk-python-0.120.2/src/anthropic/types/shared/000077500000000000000000000000001523216435200231345ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types/shared/__init__.py000066400000000000000000000015231523216435200252460ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .error_type import ErrorType as ErrorType from .error_object import ErrorObject as ErrorObject from .billing_error import BillingError as BillingError from .error_response import ErrorResponse as ErrorResponse from .not_found_error import NotFoundError as NotFoundError from .api_error_object import APIErrorObject as APIErrorObject from .overloaded_error import OverloadedError as OverloadedError from .permission_error import PermissionError as PermissionError from .rate_limit_error import RateLimitError as RateLimitError from .authentication_error import AuthenticationError as AuthenticationError from .gateway_timeout_error import GatewayTimeoutError as GatewayTimeoutError from .invalid_request_error import InvalidRequestError as InvalidRequestError anthropic-sdk-python-0.120.2/src/anthropic/types/shared/api_error_object.py000066400000000000000000000004211523216435200270130ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["APIErrorObject"] class APIErrorObject(BaseModel): message: str type: Literal["api_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/shared/authentication_error.py000066400000000000000000000004461523216435200277420ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["AuthenticationError"] class AuthenticationError(BaseModel): message: str type: Literal["authentication_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/shared/billing_error.py000066400000000000000000000004211523216435200263340ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["BillingError"] class BillingError(BaseModel): message: str type: Literal["billing_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/shared/error_object.py000066400000000000000000000017261523216435200261730ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from ..._utils import PropertyInfo from .billing_error import BillingError from .not_found_error import NotFoundError from .api_error_object import APIErrorObject from .overloaded_error import OverloadedError from .permission_error import PermissionError from .rate_limit_error import RateLimitError from .authentication_error import AuthenticationError from .gateway_timeout_error import GatewayTimeoutError from .invalid_request_error import InvalidRequestError __all__ = ["ErrorObject"] ErrorObject: TypeAlias = Annotated[ Union[ InvalidRequestError, AuthenticationError, BillingError, PermissionError, NotFoundError, RateLimitError, GatewayTimeoutError, APIErrorObject, OverloadedError, ], PropertyInfo(discriminator="type"), ] anthropic-sdk-python-0.120.2/src/anthropic/types/shared/error_response.py000066400000000000000000000005711523216435200265600ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from ..._models import BaseModel from .error_object import ErrorObject __all__ = ["ErrorResponse"] class ErrorResponse(BaseModel): error: ErrorObject request_id: Optional[str] = None type: Literal["error"] anthropic-sdk-python-0.120.2/src/anthropic/types/shared/error_type.py000066400000000000000000000006271523216435200257050ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal, TypeAlias __all__ = ["ErrorType"] ErrorType: TypeAlias = Literal[ "invalid_request_error", "authentication_error", "permission_error", "not_found_error", "rate_limit_error", "timeout_error", "overloaded_error", "api_error", "billing_error", ] anthropic-sdk-python-0.120.2/src/anthropic/types/shared/gateway_timeout_error.py000066400000000000000000000004371523216435200301320ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["GatewayTimeoutError"] class GatewayTimeoutError(BaseModel): message: str type: Literal["timeout_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/shared/invalid_request_error.py000066400000000000000000000004471523216435200301220ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["InvalidRequestError"] class InvalidRequestError(BaseModel): message: str type: Literal["invalid_request_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/shared/not_found_error.py000066400000000000000000000004251523216435200267130ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["NotFoundError"] class NotFoundError(BaseModel): message: str type: Literal["not_found_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/shared/overloaded_error.py000066400000000000000000000004321523216435200270420ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["OverloadedError"] class OverloadedError(BaseModel): message: str type: Literal["overloaded_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/shared/permission_error.py000066400000000000000000000004321523216435200271060ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["PermissionError"] class PermissionError(BaseModel): message: str type: Literal["permission_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/shared/rate_limit_error.py000066400000000000000000000004301523216435200270450ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from ..._models import BaseModel __all__ = ["RateLimitError"] class RateLimitError(BaseModel): message: str type: Literal["rate_limit_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/signature_delta.py000066400000000000000000000004301523216435200254070ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel __all__ = ["SignatureDelta"] class SignatureDelta(BaseModel): signature: str type: Literal["signature_delta"] anthropic-sdk-python-0.120.2/src/anthropic/types/stop_reason.py000066400000000000000000000004721523216435200245770ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal, TypeAlias __all__ = ["StopReason"] StopReason: TypeAlias = Literal[ "end_turn", "max_tokens", "stop_sequence", "tool_use", "pause_turn", "refusal", "model_context_window_exceeded" ] anthropic-sdk-python-0.120.2/src/anthropic/types/text_block.py000066400000000000000000000012261523216435200243770ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from typing_extensions import Literal from .._models import BaseModel from .text_citation import TextCitation __all__ = ["TextBlock"] class TextBlock(BaseModel): citations: Optional[List[TextCitation]] = None """Citations supporting the text block. The type of citation returned will depend on the type of document being cited. Citing a PDF results in `page_location`, plain text results in `char_location`, and content document results in `content_block_location`. """ text: str type: Literal["text"] anthropic-sdk-python-0.120.2/src/anthropic/types/text_block_param.py000066400000000000000000000012231523216435200255540ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Iterable, Optional from typing_extensions import Literal, Required, TypedDict from .text_citation_param import TextCitationParam from .cache_control_ephemeral_param import CacheControlEphemeralParam __all__ = ["TextBlockParam"] class TextBlockParam(TypedDict, total=False): text: Required[str] type: Required[Literal["text"]] cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" citations: Optional[Iterable[TextCitationParam]] anthropic-sdk-python-0.120.2/src/anthropic/types/text_citation.py000066400000000000000000000015221523216435200251160ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Annotated, TypeAlias from .._utils import PropertyInfo from .citation_char_location import CitationCharLocation from .citation_page_location import CitationPageLocation from .citation_content_block_location import CitationContentBlockLocation from .citations_search_result_location import CitationsSearchResultLocation from .citations_web_search_result_location import CitationsWebSearchResultLocation __all__ = ["TextCitation"] TextCitation: TypeAlias = Annotated[ Union[ CitationCharLocation, CitationPageLocation, CitationContentBlockLocation, CitationsWebSearchResultLocation, CitationsSearchResultLocation, ], PropertyInfo(discriminator="type"), ] anthropic-sdk-python-0.120.2/src/anthropic/types/text_citation_param.py000066400000000000000000000015131523216435200262760ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from typing_extensions import TypeAlias from .citation_char_location_param import CitationCharLocationParam from .citation_page_location_param import CitationPageLocationParam from .citation_content_block_location_param import CitationContentBlockLocationParam from .citation_search_result_location_param import CitationSearchResultLocationParam from .citation_web_search_result_location_param import CitationWebSearchResultLocationParam __all__ = ["TextCitationParam"] TextCitationParam: TypeAlias = Union[ CitationCharLocationParam, CitationPageLocationParam, CitationContentBlockLocationParam, CitationWebSearchResultLocationParam, CitationSearchResultLocationParam, ] anthropic-sdk-python-0.120.2/src/anthropic/types/text_delta.py000066400000000000000000000004041523216435200243730ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel __all__ = ["TextDelta"] class TextDelta(BaseModel): text: str type: Literal["text_delta"] anthropic-sdk-python-0.120.2/src/anthropic/types/text_editor_code_execution_create_result_block.py000066400000000000000000000005531523216435200337450ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel __all__ = ["TextEditorCodeExecutionCreateResultBlock"] class TextEditorCodeExecutionCreateResultBlock(BaseModel): is_file_update: bool type: Literal["text_editor_code_execution_create_result"] text_editor_code_execution_create_result_block_param.py000066400000000000000000000006561523216435200350520ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["TextEditorCodeExecutionCreateResultBlockParam"] class TextEditorCodeExecutionCreateResultBlockParam(TypedDict, total=False): is_file_update: Required[bool] type: Required[Literal["text_editor_code_execution_create_result"]] text_editor_code_execution_str_replace_result_block.py000066400000000000000000000010731523216435200347240ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Optional from typing_extensions import Literal from .._models import BaseModel __all__ = ["TextEditorCodeExecutionStrReplaceResultBlock"] class TextEditorCodeExecutionStrReplaceResultBlock(BaseModel): lines: Optional[List[str]] = None new_lines: Optional[int] = None new_start: Optional[int] = None old_lines: Optional[int] = None old_start: Optional[int] = None type: Literal["text_editor_code_execution_str_replace_result"] text_editor_code_execution_str_replace_result_block_param.py000066400000000000000000000011721523216435200361040ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict from .._types import SequenceNotStr __all__ = ["TextEditorCodeExecutionStrReplaceResultBlockParam"] class TextEditorCodeExecutionStrReplaceResultBlockParam(TypedDict, total=False): type: Required[Literal["text_editor_code_execution_str_replace_result"]] lines: Optional[SequenceNotStr[str]] new_lines: Optional[int] new_start: Optional[int] old_lines: Optional[int] old_start: Optional[int] anthropic-sdk-python-0.120.2/src/anthropic/types/text_editor_code_execution_tool_result_block.py000066400000000000000000000020221523216435200334500ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Literal, TypeAlias from .._models import BaseModel from .text_editor_code_execution_tool_result_error import TextEditorCodeExecutionToolResultError from .text_editor_code_execution_view_result_block import TextEditorCodeExecutionViewResultBlock from .text_editor_code_execution_create_result_block import TextEditorCodeExecutionCreateResultBlock from .text_editor_code_execution_str_replace_result_block import TextEditorCodeExecutionStrReplaceResultBlock __all__ = ["TextEditorCodeExecutionToolResultBlock", "Content"] Content: TypeAlias = Union[ TextEditorCodeExecutionToolResultError, TextEditorCodeExecutionViewResultBlock, TextEditorCodeExecutionCreateResultBlock, TextEditorCodeExecutionStrReplaceResultBlock, ] class TextEditorCodeExecutionToolResultBlock(BaseModel): content: Content tool_use_id: str type: Literal["text_editor_code_execution_tool_result"] text_editor_code_execution_tool_result_block_param.py000066400000000000000000000025541523216435200345630ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .cache_control_ephemeral_param import CacheControlEphemeralParam from .text_editor_code_execution_tool_result_error_param import TextEditorCodeExecutionToolResultErrorParam from .text_editor_code_execution_view_result_block_param import TextEditorCodeExecutionViewResultBlockParam from .text_editor_code_execution_create_result_block_param import TextEditorCodeExecutionCreateResultBlockParam from .text_editor_code_execution_str_replace_result_block_param import TextEditorCodeExecutionStrReplaceResultBlockParam __all__ = ["TextEditorCodeExecutionToolResultBlockParam", "Content"] Content: TypeAlias = Union[ TextEditorCodeExecutionToolResultErrorParam, TextEditorCodeExecutionViewResultBlockParam, TextEditorCodeExecutionCreateResultBlockParam, TextEditorCodeExecutionStrReplaceResultBlockParam, ] class TextEditorCodeExecutionToolResultBlockParam(TypedDict, total=False): content: Required[Content] tool_use_id: Required[str] type: Required[Literal["text_editor_code_execution_tool_result"]] cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" anthropic-sdk-python-0.120.2/src/anthropic/types/text_editor_code_execution_tool_result_error.py000066400000000000000000000010741523216435200335150ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from .._models import BaseModel from .text_editor_code_execution_tool_result_error_code import TextEditorCodeExecutionToolResultErrorCode __all__ = ["TextEditorCodeExecutionToolResultError"] class TextEditorCodeExecutionToolResultError(BaseModel): error_code: TextEditorCodeExecutionToolResultErrorCode error_message: Optional[str] = None type: Literal["text_editor_code_execution_tool_result_error"] text_editor_code_execution_tool_result_error_code.py000066400000000000000000000005601523216435200344270ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal, TypeAlias __all__ = ["TextEditorCodeExecutionToolResultErrorCode"] TextEditorCodeExecutionToolResultErrorCode: TypeAlias = Literal[ "invalid_tool_input", "unavailable", "too_many_requests", "execution_time_exceeded", "file_not_found" ] text_editor_code_execution_tool_result_error_param.py000066400000000000000000000011711523216435200346140ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict from .text_editor_code_execution_tool_result_error_code import TextEditorCodeExecutionToolResultErrorCode __all__ = ["TextEditorCodeExecutionToolResultErrorParam"] class TextEditorCodeExecutionToolResultErrorParam(TypedDict, total=False): error_code: Required[TextEditorCodeExecutionToolResultErrorCode] type: Required[Literal["text_editor_code_execution_tool_result_error"]] error_message: Optional[str] anthropic-sdk-python-0.120.2/src/anthropic/types/text_editor_code_execution_view_result_block.py000066400000000000000000000010331523216435200334460ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from .._models import BaseModel __all__ = ["TextEditorCodeExecutionViewResultBlock"] class TextEditorCodeExecutionViewResultBlock(BaseModel): content: str file_type: Literal["text", "image", "pdf"] num_lines: Optional[int] = None start_line: Optional[int] = None total_lines: Optional[int] = None type: Literal["text_editor_code_execution_view_result"] text_editor_code_execution_view_result_block_param.py000066400000000000000000000011231523216435200345470ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/src/anthropic/types# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["TextEditorCodeExecutionViewResultBlockParam"] class TextEditorCodeExecutionViewResultBlockParam(TypedDict, total=False): content: Required[str] file_type: Required[Literal["text", "image", "pdf"]] type: Required[Literal["text_editor_code_execution_view_result"]] num_lines: Optional[int] start_line: Optional[int] total_lines: Optional[int] anthropic-sdk-python-0.120.2/src/anthropic/types/thinking_block.py000066400000000000000000000004421523216435200252250ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel __all__ = ["ThinkingBlock"] class ThinkingBlock(BaseModel): signature: str thinking: str type: Literal["thinking"] anthropic-sdk-python-0.120.2/src/anthropic/types/thinking_block_param.py000066400000000000000000000005571523216435200264140ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["ThinkingBlockParam"] class ThinkingBlockParam(TypedDict, total=False): signature: Required[str] thinking: Required[str] type: Required[Literal["thinking"]] anthropic-sdk-python-0.120.2/src/anthropic/types/thinking_capability.py000066400000000000000000000006571523216435200262640ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .._models import BaseModel from .thinking_types import ThinkingTypes __all__ = ["ThinkingCapability"] class ThinkingCapability(BaseModel): """Thinking capability details.""" supported: bool """Whether this capability is supported by the model.""" types: ThinkingTypes """Supported thinking type configurations.""" anthropic-sdk-python-0.120.2/src/anthropic/types/thinking_config_adaptive_param.py000066400000000000000000000012541523216435200304370ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["ThinkingConfigAdaptiveParam"] class ThinkingConfigAdaptiveParam(TypedDict, total=False): type: Required[Literal["adaptive"]] display: Optional[Literal["summarized", "omitted"]] """Controls how thinking content appears in the response. When set to `summarized`, thinking is returned normally. When set to `omitted`, thinking content is redacted but a signature is returned for multi-turn continuity. Defaults to `summarized`. """ anthropic-sdk-python-0.120.2/src/anthropic/types/thinking_config_disabled_param.py000066400000000000000000000005061523216435200304100ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["ThinkingConfigDisabledParam"] class ThinkingConfigDisabledParam(TypedDict, total=False): type: Required[Literal["disabled"]] anthropic-sdk-python-0.120.2/src/anthropic/types/thinking_config_enabled_param.py000066400000000000000000000021001523216435200302230ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["ThinkingConfigEnabledParam"] class ThinkingConfigEnabledParam(TypedDict, total=False): budget_tokens: Required[int] """Determines how many tokens Claude can use for its internal reasoning process. Larger budgets can enable more thorough analysis for complex problems, improving response quality. Must be ≥1024 and less than `max_tokens`. See [extended thinking](https://platform.claude.com/docs/en/build-with-claude/extended-thinking) for details. """ type: Required[Literal["enabled"]] display: Optional[Literal["summarized", "omitted"]] """Controls how thinking content appears in the response. When set to `summarized`, thinking is returned normally. When set to `omitted`, thinking content is redacted but a signature is returned for multi-turn continuity. Defaults to `summarized`. """ anthropic-sdk-python-0.120.2/src/anthropic/types/thinking_config_param.py000066400000000000000000000010721523216435200265600ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from typing_extensions import TypeAlias from .thinking_config_enabled_param import ThinkingConfigEnabledParam from .thinking_config_adaptive_param import ThinkingConfigAdaptiveParam from .thinking_config_disabled_param import ThinkingConfigDisabledParam __all__ = ["ThinkingConfigParam"] ThinkingConfigParam: TypeAlias = Union[ ThinkingConfigEnabledParam, ThinkingConfigDisabledParam, ThinkingConfigAdaptiveParam ] anthropic-sdk-python-0.120.2/src/anthropic/types/thinking_delta.py000066400000000000000000000004241523216435200252240ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel __all__ = ["ThinkingDelta"] class ThinkingDelta(BaseModel): thinking: str type: Literal["thinking_delta"] anthropic-sdk-python-0.120.2/src/anthropic/types/thinking_types.py000066400000000000000000000007511523216435200253020ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from .._models import BaseModel from .capability_support import CapabilitySupport __all__ = ["ThinkingTypes"] class ThinkingTypes(BaseModel): """Supported thinking type configurations.""" adaptive: CapabilitySupport """Whether the model supports thinking with type 'adaptive' (auto).""" enabled: CapabilitySupport """Whether the model supports thinking with type 'enabled'.""" anthropic-sdk-python-0.120.2/src/anthropic/types/tool_bash_20250124_param.py000066400000000000000000000022161523216435200263520ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Iterable, Optional from typing_extensions import Literal, Required, TypedDict from .cache_control_ephemeral_param import CacheControlEphemeralParam __all__ = ["ToolBash20250124Param"] class ToolBash20250124Param(TypedDict, total=False): name: Required[Literal["bash"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["bash_20250124"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ input_examples: Iterable[Dict[str, object]] strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/tool_choice_any_param.py000066400000000000000000000010301523216435200265500ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["ToolChoiceAnyParam"] class ToolChoiceAnyParam(TypedDict, total=False): """The model will use any available tools.""" type: Required[Literal["any"]] disable_parallel_tool_use: bool """Whether to disable parallel tool use. Defaults to `false`. If set to `true`, the model will output exactly one tool use. """ anthropic-sdk-python-0.120.2/src/anthropic/types/tool_choice_auto_param.py000066400000000000000000000010551523216435200267400ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["ToolChoiceAutoParam"] class ToolChoiceAutoParam(TypedDict, total=False): """The model will automatically decide whether to use tools.""" type: Required[Literal["auto"]] disable_parallel_tool_use: bool """Whether to disable parallel tool use. Defaults to `false`. If set to `true`, the model will output at most one tool use. """ anthropic-sdk-python-0.120.2/src/anthropic/types/tool_choice_none_param.py000066400000000000000000000005511523216435200267270ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["ToolChoiceNoneParam"] class ToolChoiceNoneParam(TypedDict, total=False): """The model will not be allowed to use tools.""" type: Required[Literal["none"]] anthropic-sdk-python-0.120.2/src/anthropic/types/tool_choice_param.py000066400000000000000000000010611523216435200257050ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from typing_extensions import TypeAlias from .tool_choice_any_param import ToolChoiceAnyParam from .tool_choice_auto_param import ToolChoiceAutoParam from .tool_choice_none_param import ToolChoiceNoneParam from .tool_choice_tool_param import ToolChoiceToolParam __all__ = ["ToolChoiceParam"] ToolChoiceParam: TypeAlias = Union[ToolChoiceAutoParam, ToolChoiceAnyParam, ToolChoiceToolParam, ToolChoiceNoneParam] anthropic-sdk-python-0.120.2/src/anthropic/types/tool_choice_tool_param.py000066400000000000000000000011621523216435200267440ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["ToolChoiceToolParam"] class ToolChoiceToolParam(TypedDict, total=False): """The model will use the specified tool with `tool_choice.name`.""" name: Required[str] """The name of the tool to use.""" type: Required[Literal["tool"]] disable_parallel_tool_use: bool """Whether to disable parallel tool use. Defaults to `false`. If set to `true`, the model will output exactly one tool use. """ anthropic-sdk-python-0.120.2/src/anthropic/types/tool_param.py000066400000000000000000000053461523216435200244050ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .._types import SequenceNotStr from .._models import set_pydantic_config from .cache_control_ephemeral_param import CacheControlEphemeralParam __all__ = ["ToolParam", "InputSchema"] class InputSchemaTyped(TypedDict, total=False): """[JSON schema](https://json-schema.org/draft/2020-12) for this tool's input. This defines the shape of the `input` that your tool accepts and that the model will produce. """ type: Required[Literal["object"]] properties: Optional[Dict[str, object]] required: Optional[SequenceNotStr[str]] set_pydantic_config(InputSchemaTyped, {"extra": "allow"}) InputSchema: TypeAlias = Union[InputSchemaTyped, Dict[str, object]] class ToolParam(TypedDict, total=False): input_schema: Required[InputSchema] """[JSON schema](https://json-schema.org/draft/2020-12) for this tool's input. This defines the shape of the `input` that your tool accepts and that the model will produce. """ name: Required[str] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ description: str """Description of what this tool does. Tool descriptions should be as detailed as possible. The more information that the model has about what the tool is and how to use it, the better it will perform. You can use natural language descriptions to reinforce important aspects of the tool input JSON schema. """ eager_input_streaming: Optional[bool] """Enable eager input streaming for this tool. When true, tool input parameters will be streamed incrementally as they are generated, and types will be inferred on-the-fly rather than buffering the full JSON output. When false, streaming is disabled for this tool even if the fine-grained-tool-streaming beta is active. When null (default), uses the default behavior based on beta headers. """ input_examples: Iterable[Dict[str, object]] strict: bool """When true, guarantees schema validation on tool names and inputs""" type: Optional[Literal["custom"]] anthropic-sdk-python-0.120.2/src/anthropic/types/tool_reference_block.py000066400000000000000000000004371523216435200264110ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel __all__ = ["ToolReferenceBlock"] class ToolReferenceBlock(BaseModel): tool_name: str type: Literal["tool_reference"] anthropic-sdk-python-0.120.2/src/anthropic/types/tool_reference_block_param.py000066400000000000000000000012161523216435200275650ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict from .cache_control_ephemeral_param import CacheControlEphemeralParam __all__ = ["ToolReferenceBlockParam"] class ToolReferenceBlockParam(TypedDict, total=False): """Tool reference block that can be included in tool_result content.""" tool_name: Required[str] type: Required[Literal["tool_reference"]] cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" anthropic-sdk-python-0.120.2/src/anthropic/types/tool_result_block_param.py000066400000000000000000000020701523216435200271440ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .text_block_param import TextBlockParam from .image_block_param import ImageBlockParam from .document_block_param import DocumentBlockParam from .search_result_block_param import SearchResultBlockParam from .tool_reference_block_param import ToolReferenceBlockParam from .cache_control_ephemeral_param import CacheControlEphemeralParam __all__ = ["ToolResultBlockParam", "Content"] Content: TypeAlias = Union[ TextBlockParam, ImageBlockParam, SearchResultBlockParam, DocumentBlockParam, ToolReferenceBlockParam ] class ToolResultBlockParam(TypedDict, total=False): tool_use_id: Required[str] type: Required[Literal["tool_result"]] cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" content: Union[str, Iterable[Content]] is_error: bool anthropic-sdk-python-0.120.2/src/anthropic/types/tool_search_tool_bm25_20251119_param.py000066400000000000000000000022361523216435200305730ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Literal, Required, TypedDict from .cache_control_ephemeral_param import CacheControlEphemeralParam __all__ = ["ToolSearchToolBm25_20251119Param"] class ToolSearchToolBm25_20251119Param(TypedDict, total=False): name: Required[Literal["tool_search_tool_bm25"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["tool_search_tool_bm25_20251119", "tool_search_tool_bm25"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/tool_search_tool_regex_20251119_param.py000066400000000000000000000022411523216435200311340ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Literal, Required, TypedDict from .cache_control_ephemeral_param import CacheControlEphemeralParam __all__ = ["ToolSearchToolRegex20251119Param"] class ToolSearchToolRegex20251119Param(TypedDict, total=False): name: Required[Literal["tool_search_tool_regex"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["tool_search_tool_regex_20251119", "tool_search_tool_regex"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/tool_search_tool_result_block.py000066400000000000000000000011541523216435200303500ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union from typing_extensions import Literal, TypeAlias from .._models import BaseModel from .tool_search_tool_result_error import ToolSearchToolResultError from .tool_search_tool_search_result_block import ToolSearchToolSearchResultBlock __all__ = ["ToolSearchToolResultBlock", "Content"] Content: TypeAlias = Union[ToolSearchToolResultError, ToolSearchToolSearchResultBlock] class ToolSearchToolResultBlock(BaseModel): content: Content tool_use_id: str type: Literal["tool_search_tool_result"] anthropic-sdk-python-0.120.2/src/anthropic/types/tool_search_tool_result_block_param.py000066400000000000000000000016461523216435200315360ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .cache_control_ephemeral_param import CacheControlEphemeralParam from .tool_search_tool_result_error_param import ToolSearchToolResultErrorParam from .tool_search_tool_search_result_block_param import ToolSearchToolSearchResultBlockParam __all__ = ["ToolSearchToolResultBlockParam", "Content"] Content: TypeAlias = Union[ToolSearchToolResultErrorParam, ToolSearchToolSearchResultBlockParam] class ToolSearchToolResultBlockParam(TypedDict, total=False): content: Required[Content] tool_use_id: Required[str] type: Required[Literal["tool_search_tool_result"]] cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" anthropic-sdk-python-0.120.2/src/anthropic/types/tool_search_tool_result_error.py000066400000000000000000000007521523216435200304120ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from .._models import BaseModel from .tool_search_tool_result_error_code import ToolSearchToolResultErrorCode __all__ = ["ToolSearchToolResultError"] class ToolSearchToolResultError(BaseModel): error_code: ToolSearchToolResultErrorCode error_message: Optional[str] = None type: Literal["tool_search_tool_result_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/tool_search_tool_result_error_code.py000066400000000000000000000005041523216435200313770ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal, TypeAlias __all__ = ["ToolSearchToolResultErrorCode"] ToolSearchToolResultErrorCode: TypeAlias = Literal[ "invalid_tool_input", "unavailable", "too_many_requests", "execution_time_exceeded" ] anthropic-sdk-python-0.120.2/src/anthropic/types/tool_search_tool_result_error_param.py000066400000000000000000000010471523216435200315700ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict from .tool_search_tool_result_error_code import ToolSearchToolResultErrorCode __all__ = ["ToolSearchToolResultErrorParam"] class ToolSearchToolResultErrorParam(TypedDict, total=False): error_code: Required[ToolSearchToolResultErrorCode] type: Required[Literal["tool_search_tool_result_error"]] error_message: Optional[str] anthropic-sdk-python-0.120.2/src/anthropic/types/tool_search_tool_search_result_block.py000066400000000000000000000006611523216435200316770ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List from typing_extensions import Literal from .._models import BaseModel from .tool_reference_block import ToolReferenceBlock __all__ = ["ToolSearchToolSearchResultBlock"] class ToolSearchToolSearchResultBlock(BaseModel): tool_references: List[ToolReferenceBlock] type: Literal["tool_search_tool_search_result"] anthropic-sdk-python-0.120.2/src/anthropic/types/tool_search_tool_search_result_block_param.py000066400000000000000000000010151523216435200330510ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Iterable from typing_extensions import Literal, Required, TypedDict from .tool_reference_block_param import ToolReferenceBlockParam __all__ = ["ToolSearchToolSearchResultBlockParam"] class ToolSearchToolSearchResultBlockParam(TypedDict, total=False): tool_references: Required[Iterable[ToolReferenceBlockParam]] type: Required[Literal["tool_search_tool_search_result"]] anthropic-sdk-python-0.120.2/src/anthropic/types/tool_text_editor_20250124_param.py000066400000000000000000000022571523216435200277740ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Iterable, Optional from typing_extensions import Literal, Required, TypedDict from .cache_control_ephemeral_param import CacheControlEphemeralParam __all__ = ["ToolTextEditor20250124Param"] class ToolTextEditor20250124Param(TypedDict, total=False): name: Required[Literal["str_replace_editor"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["text_editor_20250124"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ input_examples: Iterable[Dict[str, object]] strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/tool_text_editor_20250429_param.py000066400000000000000000000022701523216435200277770ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Iterable, Optional from typing_extensions import Literal, Required, TypedDict from .cache_control_ephemeral_param import CacheControlEphemeralParam __all__ = ["ToolTextEditor20250429Param"] class ToolTextEditor20250429Param(TypedDict, total=False): name: Required[Literal["str_replace_based_edit_tool"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["text_editor_20250429"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ input_examples: Iterable[Dict[str, object]] strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/tool_text_editor_20250728_param.py000066400000000000000000000025441523216435200300050ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, List, Iterable, Optional from typing_extensions import Literal, Required, TypedDict from .cache_control_ephemeral_param import CacheControlEphemeralParam __all__ = ["ToolTextEditor20250728Param"] class ToolTextEditor20250728Param(TypedDict, total=False): name: Required[Literal["str_replace_based_edit_tool"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["text_editor_20250728"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ input_examples: Iterable[Dict[str, object]] max_characters: Optional[int] """Maximum number of characters to display when viewing a file. If not specified, defaults to displaying the full file. """ strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/tool_union_param.py000066400000000000000000000042501523216435200256060ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union from typing_extensions import TypeAlias from .tool_param import ToolParam from .tool_bash_20250124_param import ToolBash20250124Param from .memory_tool_20250818_param import MemoryTool20250818Param from .web_fetch_tool_20250910_param import WebFetchTool20250910Param from .web_fetch_tool_20260209_param import WebFetchTool20260209Param from .web_fetch_tool_20260309_param import WebFetchTool20260309Param from .web_fetch_tool_20260318_param import WebFetchTool20260318Param from .web_search_tool_20250305_param import WebSearchTool20250305Param from .web_search_tool_20260209_param import WebSearchTool20260209Param from .web_search_tool_20260318_param import WebSearchTool20260318Param from .tool_text_editor_20250124_param import ToolTextEditor20250124Param from .tool_text_editor_20250429_param import ToolTextEditor20250429Param from .tool_text_editor_20250728_param import ToolTextEditor20250728Param from .code_execution_tool_20250522_param import CodeExecutionTool20250522Param from .code_execution_tool_20250825_param import CodeExecutionTool20250825Param from .code_execution_tool_20260120_param import CodeExecutionTool20260120Param from .code_execution_tool_20260521_param import CodeExecutionTool20260521Param from .tool_search_tool_bm25_20251119_param import ToolSearchToolBm25_20251119Param from .tool_search_tool_regex_20251119_param import ToolSearchToolRegex20251119Param __all__ = ["ToolUnionParam"] ToolUnionParam: TypeAlias = Union[ ToolParam, ToolBash20250124Param, CodeExecutionTool20250522Param, CodeExecutionTool20250825Param, CodeExecutionTool20260120Param, CodeExecutionTool20260521Param, MemoryTool20250818Param, ToolTextEditor20250124Param, ToolTextEditor20250429Param, ToolTextEditor20250728Param, WebSearchTool20250305Param, WebFetchTool20250910Param, WebSearchTool20260209Param, WebFetchTool20260209Param, WebFetchTool20260309Param, WebSearchTool20260318Param, WebFetchTool20260318Param, ToolSearchToolBm25_20251119Param, ToolSearchToolRegex20251119Param, ] anthropic-sdk-python-0.120.2/src/anthropic/types/tool_use_block.py000066400000000000000000000014331523216435200252440ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Dict, Union, Optional from typing_extensions import Literal, Annotated, TypeAlias from .._utils import PropertyInfo from .._models import BaseModel from .direct_caller import DirectCaller from .server_tool_caller import ServerToolCaller from .server_tool_caller_20260120 import ServerToolCaller20260120 __all__ = ["ToolUseBlock", "Caller"] Caller: TypeAlias = Annotated[ Union[DirectCaller, ServerToolCaller, ServerToolCaller20260120], PropertyInfo(discriminator="type") ] class ToolUseBlock(BaseModel): id: str caller: Optional[Caller] = None """Tool invocation directly from the model.""" input: Dict[str, object] name: str type: Literal["tool_use"] anthropic-sdk-python-0.120.2/src/anthropic/types/tool_use_block_param.py000066400000000000000000000017621523216435200264310ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Dict, Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .direct_caller_param import DirectCallerParam from .server_tool_caller_param import ServerToolCallerParam from .cache_control_ephemeral_param import CacheControlEphemeralParam from .server_tool_caller_20260120_param import ServerToolCaller20260120Param __all__ = ["ToolUseBlockParam", "Caller"] Caller: TypeAlias = Union[DirectCallerParam, ServerToolCallerParam, ServerToolCaller20260120Param] class ToolUseBlockParam(TypedDict, total=False): id: Required[str] input: Required[Dict[str, object]] name: Required[str] type: Required[Literal["tool_use"]] cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" caller: Caller """Tool invocation directly from the model.""" anthropic-sdk-python-0.120.2/src/anthropic/types/url_image_source_param.py000066400000000000000000000005111523216435200267410ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["URLImageSourceParam"] class URLImageSourceParam(TypedDict, total=False): type: Required[Literal["url"]] url: Required[str] anthropic-sdk-python-0.120.2/src/anthropic/types/url_pdf_source_param.py000066400000000000000000000005051523216435200264330ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict __all__ = ["URLPDFSourceParam"] class URLPDFSourceParam(TypedDict, total=False): type: Required[Literal["url"]] url: Required[str] anthropic-sdk-python-0.120.2/src/anthropic/types/usage.py000066400000000000000000000031671523216435200233530ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from .._models import BaseModel from .cache_creation import CacheCreation from .server_tool_usage import ServerToolUsage from .output_tokens_details import OutputTokensDetails __all__ = ["Usage"] class Usage(BaseModel): cache_creation: Optional[CacheCreation] = None """Breakdown of cached tokens by TTL""" cache_creation_input_tokens: Optional[int] = None """The number of input tokens used to create the cache entry.""" cache_read_input_tokens: Optional[int] = None """The number of input tokens read from the cache.""" inference_geo: Optional[str] = None """The geographic region where inference was performed for this request.""" input_tokens: int """The number of input tokens which were used.""" output_tokens: int """The number of output tokens which were used.""" output_tokens_details: Optional[OutputTokensDetails] = None """Breakdown of output tokens by category. `output_tokens` remains the inclusive, authoritative total used for billing. This object provides a read-only decomposition for observability — for example, how many of the billed output tokens were spent on internal reasoning that may have been summarized before being returned to you. """ server_tool_use: Optional[ServerToolUsage] = None """The number of server tool requests.""" service_tier: Optional[Literal["standard", "priority", "batch"]] = None """If the request used the priority, standard, or batch tier.""" anthropic-sdk-python-0.120.2/src/anthropic/types/user_location_param.py000066400000000000000000000013101523216435200262610ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["UserLocationParam"] class UserLocationParam(TypedDict, total=False): type: Required[Literal["approximate"]] city: Optional[str] """The city of the user.""" country: Optional[str] """ The two letter [ISO country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) of the user. """ region: Optional[str] """The region of the user.""" timezone: Optional[str] """The [IANA timezone](https://nodatime.org/TimeZones) of the user.""" anthropic-sdk-python-0.120.2/src/anthropic/types/web_fetch_block.py000066400000000000000000000007651523216435200253500ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from .._models import BaseModel from .document_block import DocumentBlock __all__ = ["WebFetchBlock"] class WebFetchBlock(BaseModel): content: DocumentBlock retrieved_at: Optional[str] = None """ISO 8601 timestamp when the content was retrieved""" type: Literal["web_fetch_result"] url: str """Fetched content URL""" anthropic-sdk-python-0.120.2/src/anthropic/types/web_fetch_block_param.py000066400000000000000000000011141523216435200265150ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict from .document_block_param import DocumentBlockParam __all__ = ["WebFetchBlockParam"] class WebFetchBlockParam(TypedDict, total=False): content: Required[DocumentBlockParam] type: Required[Literal["web_fetch_result"]] url: Required[str] """Fetched content URL""" retrieved_at: Optional[str] """ISO 8601 timestamp when the content was retrieved""" anthropic-sdk-python-0.120.2/src/anthropic/types/web_fetch_tool_20250910_param.py000066400000000000000000000035331523216435200273710ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Literal, Required, TypedDict from .._types import SequenceNotStr from .citations_config_param import CitationsConfigParam from .cache_control_ephemeral_param import CacheControlEphemeralParam __all__ = ["WebFetchTool20250910Param"] class WebFetchTool20250910Param(TypedDict, total=False): name: Required[Literal["web_fetch"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["web_fetch_20250910"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] allowed_domains: Optional[SequenceNotStr[str]] """List of domains to allow fetching from""" blocked_domains: Optional[SequenceNotStr[str]] """List of domains to block fetching from""" cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" citations: Optional[CitationsConfigParam] """Citations configuration for fetched documents. Citations are disabled by default. """ defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ max_content_tokens: Optional[int] """Maximum number of tokens used by including web page text content in the context. The limit is approximate and does not apply to binary content such as PDFs. """ max_uses: Optional[int] """Maximum number of times the tool can be used in the API request.""" strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/web_fetch_tool_20260209_param.py000066400000000000000000000035331523216435200273730ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Literal, Required, TypedDict from .._types import SequenceNotStr from .citations_config_param import CitationsConfigParam from .cache_control_ephemeral_param import CacheControlEphemeralParam __all__ = ["WebFetchTool20260209Param"] class WebFetchTool20260209Param(TypedDict, total=False): name: Required[Literal["web_fetch"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["web_fetch_20260209"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] allowed_domains: Optional[SequenceNotStr[str]] """List of domains to allow fetching from""" blocked_domains: Optional[SequenceNotStr[str]] """List of domains to block fetching from""" cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" citations: Optional[CitationsConfigParam] """Citations configuration for fetched documents. Citations are disabled by default. """ defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ max_content_tokens: Optional[int] """Maximum number of tokens used by including web page text content in the context. The limit is approximate and does not apply to binary content such as PDFs. """ max_uses: Optional[int] """Maximum number of times the tool can be used in the API request.""" strict: bool """When true, guarantees schema validation on tool names and inputs""" anthropic-sdk-python-0.120.2/src/anthropic/types/web_fetch_tool_20260309_param.py000066400000000000000000000042431523216435200273730ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Literal, Required, TypedDict from .._types import SequenceNotStr from .citations_config_param import CitationsConfigParam from .cache_control_ephemeral_param import CacheControlEphemeralParam __all__ = ["WebFetchTool20260309Param"] class WebFetchTool20260309Param(TypedDict, total=False): """Web fetch tool with use_cache parameter for bypassing cached content.""" name: Required[Literal["web_fetch"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["web_fetch_20260309"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] allowed_domains: Optional[SequenceNotStr[str]] """List of domains to allow fetching from""" blocked_domains: Optional[SequenceNotStr[str]] """List of domains to block fetching from""" cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" citations: Optional[CitationsConfigParam] """Citations configuration for fetched documents. Citations are disabled by default. """ defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ max_content_tokens: Optional[int] """Maximum number of tokens used by including web page text content in the context. The limit is approximate and does not apply to binary content such as PDFs. """ max_uses: Optional[int] """Maximum number of times the tool can be used in the API request.""" strict: bool """When true, guarantees schema validation on tool names and inputs""" use_cache: bool """Whether to use cached content. Set to false to bypass the cache and fetch fresh content. Only set to false when the user explicitly requests fresh content or when fetching rapidly-changing sources. """ anthropic-sdk-python-0.120.2/src/anthropic/types/web_fetch_tool_20260318_param.py000066400000000000000000000051231523216435200273710ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Literal, Required, TypedDict from .._types import SequenceNotStr from .citations_config_param import CitationsConfigParam from .cache_control_ephemeral_param import CacheControlEphemeralParam __all__ = ["WebFetchTool20260318Param"] class WebFetchTool20260318Param(TypedDict, total=False): name: Required[Literal["web_fetch"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["web_fetch_20260318"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] allowed_domains: Optional[SequenceNotStr[str]] """List of domains to allow fetching from""" blocked_domains: Optional[SequenceNotStr[str]] """List of domains to block fetching from""" cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" citations: Optional[CitationsConfigParam] """Citations configuration for fetched documents. Citations are disabled by default. """ defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ max_content_tokens: Optional[int] """Maximum number of tokens used by including web page text content in the context. The limit is approximate and does not apply to binary content such as PDFs. """ max_uses: Optional[int] """Maximum number of times the tool can be used in the API request.""" response_inclusion: Literal["full", "excluded"] """ How this tool's result blocks appear in the API response when the result was consumed by a completed code_execution call in the same turn. 'full' returns the complete content (default). 'excluded' drops the nested server_tool_use and result block pair entirely. Results from direct calls, or from code_execution calls that paused before completing, are always returned in full so they can be sent back on the next turn. """ strict: bool """When true, guarantees schema validation on tool names and inputs""" use_cache: bool """Whether to use cached content. Set to false to bypass the cache and fetch fresh content. Only set to false when the user explicitly requests fresh content or when fetching rapidly-changing sources. """ anthropic-sdk-python-0.120.2/src/anthropic/types/web_fetch_tool_result_block.py000066400000000000000000000017651523216435200300040ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union, Optional from typing_extensions import Literal, Annotated, TypeAlias from .._utils import PropertyInfo from .._models import BaseModel from .direct_caller import DirectCaller from .web_fetch_block import WebFetchBlock from .server_tool_caller import ServerToolCaller from .server_tool_caller_20260120 import ServerToolCaller20260120 from .web_fetch_tool_result_error_block import WebFetchToolResultErrorBlock __all__ = ["WebFetchToolResultBlock", "Caller", "Content"] Caller: TypeAlias = Annotated[ Union[DirectCaller, ServerToolCaller, ServerToolCaller20260120], PropertyInfo(discriminator="type") ] Content: TypeAlias = Union[WebFetchToolResultErrorBlock, WebFetchBlock] class WebFetchToolResultBlock(BaseModel): caller: Optional[Caller] = None """Tool invocation directly from the model.""" content: Content tool_use_id: str type: Literal["web_fetch_tool_result"] anthropic-sdk-python-0.120.2/src/anthropic/types/web_fetch_tool_result_block_param.py000066400000000000000000000023421523216435200311540ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .direct_caller_param import DirectCallerParam from .web_fetch_block_param import WebFetchBlockParam from .server_tool_caller_param import ServerToolCallerParam from .cache_control_ephemeral_param import CacheControlEphemeralParam from .server_tool_caller_20260120_param import ServerToolCaller20260120Param from .web_fetch_tool_result_error_block_param import WebFetchToolResultErrorBlockParam __all__ = ["WebFetchToolResultBlockParam", "Content", "Caller"] Content: TypeAlias = Union[WebFetchToolResultErrorBlockParam, WebFetchBlockParam] Caller: TypeAlias = Union[DirectCallerParam, ServerToolCallerParam, ServerToolCaller20260120Param] class WebFetchToolResultBlockParam(TypedDict, total=False): content: Required[Content] tool_use_id: Required[str] type: Required[Literal["web_fetch_tool_result"]] cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" caller: Caller """Tool invocation directly from the model.""" anthropic-sdk-python-0.120.2/src/anthropic/types/web_fetch_tool_result_error_block.py000066400000000000000000000006431523216435200312070ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel from .web_fetch_tool_result_error_code import WebFetchToolResultErrorCode __all__ = ["WebFetchToolResultErrorBlock"] class WebFetchToolResultErrorBlock(BaseModel): error_code: WebFetchToolResultErrorCode type: Literal["web_fetch_tool_result_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/web_fetch_tool_result_error_block_param.py000066400000000000000000000007471523216435200323740ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict from .web_fetch_tool_result_error_code import WebFetchToolResultErrorCode __all__ = ["WebFetchToolResultErrorBlockParam"] class WebFetchToolResultErrorBlockParam(TypedDict, total=False): error_code: Required[WebFetchToolResultErrorCode] type: Required[Literal["web_fetch_tool_result_error"]] anthropic-sdk-python-0.120.2/src/anthropic/types/web_fetch_tool_result_error_code.py000066400000000000000000000007141523216435200310260ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal, TypeAlias __all__ = ["WebFetchToolResultErrorCode"] WebFetchToolResultErrorCode: TypeAlias = Literal[ "invalid_tool_input", "url_too_long", "url_not_allowed", "url_not_in_prior_context", "url_not_accessible", "unsupported_content_type", "too_many_requests", "max_uses_exceeded", "unavailable", ] anthropic-sdk-python-0.120.2/src/anthropic/types/web_search_result_block.py000066400000000000000000000006141523216435200271130ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Optional from typing_extensions import Literal from .._models import BaseModel __all__ = ["WebSearchResultBlock"] class WebSearchResultBlock(BaseModel): encrypted_content: str page_age: Optional[str] = None title: str type: Literal["web_search_result"] url: str anthropic-sdk-python-0.120.2/src/anthropic/types/web_search_result_block_param.py000066400000000000000000000007341523216435200302760ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Optional from typing_extensions import Literal, Required, TypedDict __all__ = ["WebSearchResultBlockParam"] class WebSearchResultBlockParam(TypedDict, total=False): encrypted_content: Required[str] title: Required[str] type: Required[Literal["web_search_result"]] url: Required[str] page_age: Optional[str] anthropic-sdk-python-0.120.2/src/anthropic/types/web_search_tool_20250305_param.py000066400000000000000000000035421523216435200275430ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Literal, Required, TypedDict from .._types import SequenceNotStr from .user_location_param import UserLocationParam from .cache_control_ephemeral_param import CacheControlEphemeralParam __all__ = ["WebSearchTool20250305Param", "UserLocation"] class WebSearchTool20250305Param(TypedDict, total=False): name: Required[Literal["web_search"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["web_search_20250305"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] allowed_domains: Optional[SequenceNotStr[str]] """If provided, only these domains will be included in results. Cannot be used alongside `blocked_domains`. """ blocked_domains: Optional[SequenceNotStr[str]] """If provided, these domains will never appear in results. Cannot be used alongside `allowed_domains`. """ cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ max_uses: Optional[int] """Maximum number of times the tool can be used in the API request.""" strict: bool """When true, guarantees schema validation on tool names and inputs""" user_location: Optional[UserLocationParam] """Parameters for the user's location. Used to provide more relevant search results. """ UserLocation = UserLocationParam # backward compat alias anthropic-sdk-python-0.120.2/src/anthropic/types/web_search_tool_20260209_param.py000066400000000000000000000035421523216435200275470ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Literal, Required, TypedDict from .._types import SequenceNotStr from .user_location_param import UserLocationParam from .cache_control_ephemeral_param import CacheControlEphemeralParam __all__ = ["WebSearchTool20260209Param", "UserLocation"] class WebSearchTool20260209Param(TypedDict, total=False): name: Required[Literal["web_search"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["web_search_20260209"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] allowed_domains: Optional[SequenceNotStr[str]] """If provided, only these domains will be included in results. Cannot be used alongside `blocked_domains`. """ blocked_domains: Optional[SequenceNotStr[str]] """If provided, these domains will never appear in results. Cannot be used alongside `allowed_domains`. """ cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ max_uses: Optional[int] """Maximum number of times the tool can be used in the API request.""" strict: bool """When true, guarantees schema validation on tool names and inputs""" user_location: Optional[UserLocationParam] """Parameters for the user's location. Used to provide more relevant search results. """ UserLocation = UserLocationParam # backward compat alias anthropic-sdk-python-0.120.2/src/anthropic/types/web_search_tool_20260318_param.py000066400000000000000000000044271523216435200275530ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import List, Optional from typing_extensions import Literal, Required, TypedDict from .._types import SequenceNotStr from .user_location_param import UserLocationParam from .cache_control_ephemeral_param import CacheControlEphemeralParam __all__ = ["WebSearchTool20260318Param"] class WebSearchTool20260318Param(TypedDict, total=False): name: Required[Literal["web_search"]] """Name of the tool. This is how the tool will be called by the model and in `tool_use` blocks. """ type: Required[Literal["web_search_20260318"]] allowed_callers: List[ Literal["direct", "code_execution_20250825", "code_execution_20260120", "code_execution_20260521"] ] allowed_domains: Optional[SequenceNotStr[str]] """If provided, only these domains will be included in results. Cannot be used alongside `blocked_domains`. """ blocked_domains: Optional[SequenceNotStr[str]] """If provided, these domains will never appear in results. Cannot be used alongside `allowed_domains`. """ cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" defer_loading: bool """If true, tool will not be included in initial system prompt. Only loaded when returned via tool_reference from tool search. """ max_uses: Optional[int] """Maximum number of times the tool can be used in the API request.""" response_inclusion: Literal["full", "excluded"] """ How this tool's result blocks appear in the API response when the result was consumed by a completed code_execution call in the same turn. 'full' returns the complete content (default). 'excluded' drops the nested server_tool_use and result block pair entirely. Results from direct calls, or from code_execution calls that paused before completing, are always returned in full so they can be sent back on the next turn. """ strict: bool """When true, guarantees schema validation on tool names and inputs""" user_location: Optional[UserLocationParam] """Parameters for the user's location. Used to provide more relevant search results. """ anthropic-sdk-python-0.120.2/src/anthropic/types/web_search_tool_request_error_param.py000066400000000000000000000007451523216435200315460ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing_extensions import Literal, Required, TypedDict from .web_search_tool_result_error_code import WebSearchToolResultErrorCode __all__ = ["WebSearchToolRequestErrorParam"] class WebSearchToolRequestErrorParam(TypedDict, total=False): error_code: Required[WebSearchToolResultErrorCode] type: Required[Literal["web_search_tool_result_error"]] anthropic-sdk-python-0.120.2/src/anthropic/types/web_search_tool_result_block.py000066400000000000000000000016271523216435200301550ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import Union, Optional from typing_extensions import Literal, Annotated, TypeAlias from .._utils import PropertyInfo from .._models import BaseModel from .direct_caller import DirectCaller from .server_tool_caller import ServerToolCaller from .server_tool_caller_20260120 import ServerToolCaller20260120 from .web_search_tool_result_block_content import WebSearchToolResultBlockContent __all__ = ["WebSearchToolResultBlock", "Caller"] Caller: TypeAlias = Annotated[ Union[DirectCaller, ServerToolCaller, ServerToolCaller20260120], PropertyInfo(discriminator="type") ] class WebSearchToolResultBlock(BaseModel): caller: Optional[Caller] = None """Tool invocation directly from the model.""" content: WebSearchToolResultBlockContent tool_use_id: str type: Literal["web_search_tool_result"] anthropic-sdk-python-0.120.2/src/anthropic/types/web_search_tool_result_block_content.py000066400000000000000000000006651523216435200317100ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing import List, Union from typing_extensions import TypeAlias from .web_search_result_block import WebSearchResultBlock from .web_search_tool_result_error import WebSearchToolResultError __all__ = ["WebSearchToolResultBlockContent"] WebSearchToolResultBlockContent: TypeAlias = Union[WebSearchToolResultError, List[WebSearchResultBlock]] anthropic-sdk-python-0.120.2/src/anthropic/types/web_search_tool_result_block_param.py000066400000000000000000000022041523216435200313250ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict from .direct_caller_param import DirectCallerParam from .server_tool_caller_param import ServerToolCallerParam from .cache_control_ephemeral_param import CacheControlEphemeralParam from .server_tool_caller_20260120_param import ServerToolCaller20260120Param from .web_search_tool_result_block_param_content_param import WebSearchToolResultBlockParamContentParam __all__ = ["WebSearchToolResultBlockParam", "Caller"] Caller: TypeAlias = Union[DirectCallerParam, ServerToolCallerParam, ServerToolCaller20260120Param] class WebSearchToolResultBlockParam(TypedDict, total=False): content: Required[WebSearchToolResultBlockParamContentParam] tool_use_id: Required[str] type: Required[Literal["web_search_tool_result"]] cache_control: Optional[CacheControlEphemeralParam] """Create a cache control breakpoint at this content block.""" caller: Caller """Tool invocation directly from the model.""" anthropic-sdk-python-0.120.2/src/anthropic/types/web_search_tool_result_block_param_content_param.py000066400000000000000000000010361523216435200342410ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations from typing import Union, Iterable from typing_extensions import TypeAlias from .web_search_result_block_param import WebSearchResultBlockParam from .web_search_tool_request_error_param import WebSearchToolRequestErrorParam __all__ = ["WebSearchToolResultBlockParamContentParam"] WebSearchToolResultBlockParamContentParam: TypeAlias = Union[ Iterable[WebSearchResultBlockParam], WebSearchToolRequestErrorParam ] anthropic-sdk-python-0.120.2/src/anthropic/types/web_search_tool_result_error.py000066400000000000000000000006371523216435200302140ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal from .._models import BaseModel from .web_search_tool_result_error_code import WebSearchToolResultErrorCode __all__ = ["WebSearchToolResultError"] class WebSearchToolResultError(BaseModel): error_code: WebSearchToolResultErrorCode type: Literal["web_search_tool_result_error"] anthropic-sdk-python-0.120.2/src/anthropic/types/web_search_tool_result_error_code.py000066400000000000000000000005431523216435200312020ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from typing_extensions import Literal, TypeAlias __all__ = ["WebSearchToolResultErrorCode"] WebSearchToolResultErrorCode: TypeAlias = Literal[ "invalid_tool_input", "unavailable", "max_uses_exceeded", "too_many_requests", "query_too_long", "request_too_large" ] anthropic-sdk-python-0.120.2/tests/000077500000000000000000000000001523216435200171065ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/__init__.py000066400000000000000000000001261523216435200212160ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. anthropic-sdk-python-0.120.2/tests/api_resources/000077500000000000000000000000001523216435200217515ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/api_resources/__init__.py000066400000000000000000000001261523216435200240610ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. anthropic-sdk-python-0.120.2/tests/api_resources/beta/000077500000000000000000000000001523216435200226645ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/api_resources/beta/__init__.py000066400000000000000000000001261523216435200247740ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. anthropic-sdk-python-0.120.2/tests/api_resources/beta/agents/000077500000000000000000000000001523216435200241455ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/api_resources/beta/agents/__init__.py000066400000000000000000000001261523216435200262550ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. anthropic-sdk-python-0.120.2/tests/api_resources/beta/agents/test_versions.py000066400000000000000000000132051523216435200274270ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os from typing import Any, cast import pytest from anthropic import Anthropic, AsyncAnthropic from tests.utils import assert_matches_type from anthropic.pagination import SyncPageCursor, AsyncPageCursor from anthropic.types.beta import BetaManagedAgentsAgent base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestVersions: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list(self, client: Anthropic) -> None: version = client.beta.agents.versions.list( agent_id="agent_011CZkYpogX7uDKUyvBTophP", ) assert_matches_type(SyncPageCursor[BetaManagedAgentsAgent], version, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list_with_all_params(self, client: Anthropic) -> None: version = client.beta.agents.versions.list( agent_id="agent_011CZkYpogX7uDKUyvBTophP", limit=0, page="page", betas=["string"], ) assert_matches_type(SyncPageCursor[BetaManagedAgentsAgent], version, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_raw_response_list(self, client: Anthropic) -> None: response = client.beta.agents.versions.with_raw_response.list( agent_id="agent_011CZkYpogX7uDKUyvBTophP", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" version = response.parse() assert_matches_type(SyncPageCursor[BetaManagedAgentsAgent], version, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_streaming_response_list(self, client: Anthropic) -> None: with client.beta.agents.versions.with_streaming_response.list( agent_id="agent_011CZkYpogX7uDKUyvBTophP", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" version = response.parse() assert_matches_type(SyncPageCursor[BetaManagedAgentsAgent], version, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_path_params_list(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_id` but received ''"): client.beta.agents.versions.with_raw_response.list( agent_id="", ) class TestAsyncVersions: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list(self, async_client: AsyncAnthropic) -> None: version = await async_client.beta.agents.versions.list( agent_id="agent_011CZkYpogX7uDKUyvBTophP", ) assert_matches_type(AsyncPageCursor[BetaManagedAgentsAgent], version, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncAnthropic) -> None: version = await async_client.beta.agents.versions.list( agent_id="agent_011CZkYpogX7uDKUyvBTophP", limit=0, page="page", betas=["string"], ) assert_matches_type(AsyncPageCursor[BetaManagedAgentsAgent], version, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_raw_response_list(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.agents.versions.with_raw_response.list( agent_id="agent_011CZkYpogX7uDKUyvBTophP", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" version = response.parse() assert_matches_type(AsyncPageCursor[BetaManagedAgentsAgent], version, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_streaming_response_list(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.agents.versions.with_streaming_response.list( agent_id="agent_011CZkYpogX7uDKUyvBTophP", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" version = await response.parse() assert_matches_type(AsyncPageCursor[BetaManagedAgentsAgent], version, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_path_params_list(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_id` but received ''"): await async_client.beta.agents.versions.with_raw_response.list( agent_id="", ) anthropic-sdk-python-0.120.2/tests/api_resources/beta/environments/000077500000000000000000000000001523216435200254135ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/api_resources/beta/environments/__init__.py000066400000000000000000000001261523216435200275230ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. anthropic-sdk-python-0.120.2/tests/api_resources/beta/environments/test_work.py000066400000000000000000001170151523216435200300130ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os from typing import Any, Optional, cast import pytest from anthropic import Anthropic, AsyncAnthropic from tests.utils import assert_matches_type from anthropic.pagination import SyncPageCursor, AsyncPageCursor from anthropic.types.beta.environments import ( BetaSelfHostedWork, BetaSelfHostedWorkQueueStats, BetaSelfHostedWorkHeartbeatResponse, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestWork: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_retrieve(self, client: Anthropic) -> None: work = client.beta.environments.work.retrieve( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert_matches_type(BetaSelfHostedWork, work, path=["response"]) @parametrize def test_method_retrieve_with_all_params(self, client: Anthropic) -> None: work = client.beta.environments.work.retrieve( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaSelfHostedWork, work, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: Anthropic) -> None: response = client.beta.environments.work.with_raw_response.retrieve( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = response.parse() assert_matches_type(BetaSelfHostedWork, work, path=["response"]) @parametrize def test_streaming_response_retrieve(self, client: Anthropic) -> None: with client.beta.environments.work.with_streaming_response.retrieve( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = response.parse() assert_matches_type(BetaSelfHostedWork, work, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_retrieve(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"): client.beta.environments.work.with_raw_response.retrieve( work_id="work_id", environment_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `work_id` but received ''"): client.beta.environments.work.with_raw_response.retrieve( work_id="", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) @parametrize def test_method_update(self, client: Anthropic) -> None: work = client.beta.environments.work.update( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", metadata={"foo": "string"}, ) assert_matches_type(BetaSelfHostedWork, work, path=["response"]) @parametrize def test_method_update_with_all_params(self, client: Anthropic) -> None: work = client.beta.environments.work.update( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", metadata={"foo": "string"}, betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaSelfHostedWork, work, path=["response"]) @parametrize def test_raw_response_update(self, client: Anthropic) -> None: response = client.beta.environments.work.with_raw_response.update( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", metadata={"foo": "string"}, ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = response.parse() assert_matches_type(BetaSelfHostedWork, work, path=["response"]) @parametrize def test_streaming_response_update(self, client: Anthropic) -> None: with client.beta.environments.work.with_streaming_response.update( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", metadata={"foo": "string"}, ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = response.parse() assert_matches_type(BetaSelfHostedWork, work, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_update(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"): client.beta.environments.work.with_raw_response.update( work_id="work_id", environment_id="", metadata={"foo": "string"}, ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `work_id` but received ''"): client.beta.environments.work.with_raw_response.update( work_id="", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", metadata={"foo": "string"}, ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list(self, client: Anthropic) -> None: work = client.beta.environments.work.list( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert_matches_type(SyncPageCursor[BetaSelfHostedWork], work, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list_with_all_params(self, client: Anthropic) -> None: work = client.beta.environments.work.list( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", limit=1, page="page", betas=["message-batches-2024-09-24"], ) assert_matches_type(SyncPageCursor[BetaSelfHostedWork], work, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_raw_response_list(self, client: Anthropic) -> None: response = client.beta.environments.work.with_raw_response.list( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = response.parse() assert_matches_type(SyncPageCursor[BetaSelfHostedWork], work, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_streaming_response_list(self, client: Anthropic) -> None: with client.beta.environments.work.with_streaming_response.list( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = response.parse() assert_matches_type(SyncPageCursor[BetaSelfHostedWork], work, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_path_params_list(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"): client.beta.environments.work.with_raw_response.list( environment_id="", ) @parametrize def test_method_ack(self, client: Anthropic) -> None: work = client.beta.environments.work.ack( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert_matches_type(BetaSelfHostedWork, work, path=["response"]) @parametrize def test_method_ack_with_all_params(self, client: Anthropic) -> None: work = client.beta.environments.work.ack( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaSelfHostedWork, work, path=["response"]) @parametrize def test_raw_response_ack(self, client: Anthropic) -> None: response = client.beta.environments.work.with_raw_response.ack( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = response.parse() assert_matches_type(BetaSelfHostedWork, work, path=["response"]) @parametrize def test_streaming_response_ack(self, client: Anthropic) -> None: with client.beta.environments.work.with_streaming_response.ack( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = response.parse() assert_matches_type(BetaSelfHostedWork, work, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_ack(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"): client.beta.environments.work.with_raw_response.ack( work_id="work_id", environment_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `work_id` but received ''"): client.beta.environments.work.with_raw_response.ack( work_id="", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) @parametrize def test_method_heartbeat(self, client: Anthropic) -> None: work = client.beta.environments.work.heartbeat( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert_matches_type(BetaSelfHostedWorkHeartbeatResponse, work, path=["response"]) @parametrize def test_method_heartbeat_with_all_params(self, client: Anthropic) -> None: work = client.beta.environments.work.heartbeat( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", desired_ttl_seconds=0, expected_last_heartbeat="expected_last_heartbeat", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaSelfHostedWorkHeartbeatResponse, work, path=["response"]) @parametrize def test_raw_response_heartbeat(self, client: Anthropic) -> None: response = client.beta.environments.work.with_raw_response.heartbeat( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = response.parse() assert_matches_type(BetaSelfHostedWorkHeartbeatResponse, work, path=["response"]) @parametrize def test_streaming_response_heartbeat(self, client: Anthropic) -> None: with client.beta.environments.work.with_streaming_response.heartbeat( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = response.parse() assert_matches_type(BetaSelfHostedWorkHeartbeatResponse, work, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_heartbeat(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"): client.beta.environments.work.with_raw_response.heartbeat( work_id="work_id", environment_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `work_id` but received ''"): client.beta.environments.work.with_raw_response.heartbeat( work_id="", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) @parametrize def test_method_poll(self, client: Anthropic) -> None: work = client.beta.environments.work.poll( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert_matches_type(Optional[BetaSelfHostedWork], work, path=["response"]) @parametrize def test_method_poll_with_all_params(self, client: Anthropic) -> None: work = client.beta.environments.work.poll( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", block_ms=1, reclaim_older_than_ms=1, betas=["message-batches-2024-09-24"], anthropic_worker_id="Anthropic-Worker-ID", ) assert_matches_type(Optional[BetaSelfHostedWork], work, path=["response"]) @parametrize def test_raw_response_poll(self, client: Anthropic) -> None: response = client.beta.environments.work.with_raw_response.poll( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = response.parse() assert_matches_type(Optional[BetaSelfHostedWork], work, path=["response"]) @parametrize def test_streaming_response_poll(self, client: Anthropic) -> None: with client.beta.environments.work.with_streaming_response.poll( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = response.parse() assert_matches_type(Optional[BetaSelfHostedWork], work, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_poll(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"): client.beta.environments.work.with_raw_response.poll( environment_id="", ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_stats(self, client: Anthropic) -> None: work = client.beta.environments.work.stats( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert_matches_type(BetaSelfHostedWorkQueueStats, work, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_stats_with_all_params(self, client: Anthropic) -> None: work = client.beta.environments.work.stats( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaSelfHostedWorkQueueStats, work, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_raw_response_stats(self, client: Anthropic) -> None: response = client.beta.environments.work.with_raw_response.stats( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = response.parse() assert_matches_type(BetaSelfHostedWorkQueueStats, work, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_streaming_response_stats(self, client: Anthropic) -> None: with client.beta.environments.work.with_streaming_response.stats( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = response.parse() assert_matches_type(BetaSelfHostedWorkQueueStats, work, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_path_params_stats(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"): client.beta.environments.work.with_raw_response.stats( environment_id="", ) @parametrize def test_method_stop(self, client: Anthropic) -> None: work = client.beta.environments.work.stop( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert_matches_type(BetaSelfHostedWork, work, path=["response"]) @parametrize def test_method_stop_with_all_params(self, client: Anthropic) -> None: work = client.beta.environments.work.stop( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", force=True, betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaSelfHostedWork, work, path=["response"]) @parametrize def test_raw_response_stop(self, client: Anthropic) -> None: response = client.beta.environments.work.with_raw_response.stop( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = response.parse() assert_matches_type(BetaSelfHostedWork, work, path=["response"]) @parametrize def test_streaming_response_stop(self, client: Anthropic) -> None: with client.beta.environments.work.with_streaming_response.stop( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = response.parse() assert_matches_type(BetaSelfHostedWork, work, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_stop(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"): client.beta.environments.work.with_raw_response.stop( work_id="work_id", environment_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `work_id` but received ''"): client.beta.environments.work.with_raw_response.stop( work_id="", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) class TestAsyncWork: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize async def test_method_retrieve(self, async_client: AsyncAnthropic) -> None: work = await async_client.beta.environments.work.retrieve( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert_matches_type(BetaSelfHostedWork, work, path=["response"]) @parametrize async def test_method_retrieve_with_all_params(self, async_client: AsyncAnthropic) -> None: work = await async_client.beta.environments.work.retrieve( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaSelfHostedWork, work, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.environments.work.with_raw_response.retrieve( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = response.parse() assert_matches_type(BetaSelfHostedWork, work, path=["response"]) @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.environments.work.with_streaming_response.retrieve( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = await response.parse() assert_matches_type(BetaSelfHostedWork, work, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_retrieve(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"): await async_client.beta.environments.work.with_raw_response.retrieve( work_id="work_id", environment_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `work_id` but received ''"): await async_client.beta.environments.work.with_raw_response.retrieve( work_id="", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) @parametrize async def test_method_update(self, async_client: AsyncAnthropic) -> None: work = await async_client.beta.environments.work.update( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", metadata={"foo": "string"}, ) assert_matches_type(BetaSelfHostedWork, work, path=["response"]) @parametrize async def test_method_update_with_all_params(self, async_client: AsyncAnthropic) -> None: work = await async_client.beta.environments.work.update( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", metadata={"foo": "string"}, betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaSelfHostedWork, work, path=["response"]) @parametrize async def test_raw_response_update(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.environments.work.with_raw_response.update( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", metadata={"foo": "string"}, ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = response.parse() assert_matches_type(BetaSelfHostedWork, work, path=["response"]) @parametrize async def test_streaming_response_update(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.environments.work.with_streaming_response.update( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", metadata={"foo": "string"}, ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = await response.parse() assert_matches_type(BetaSelfHostedWork, work, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_update(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"): await async_client.beta.environments.work.with_raw_response.update( work_id="work_id", environment_id="", metadata={"foo": "string"}, ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `work_id` but received ''"): await async_client.beta.environments.work.with_raw_response.update( work_id="", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", metadata={"foo": "string"}, ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list(self, async_client: AsyncAnthropic) -> None: work = await async_client.beta.environments.work.list( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert_matches_type(AsyncPageCursor[BetaSelfHostedWork], work, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncAnthropic) -> None: work = await async_client.beta.environments.work.list( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", limit=1, page="page", betas=["message-batches-2024-09-24"], ) assert_matches_type(AsyncPageCursor[BetaSelfHostedWork], work, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_raw_response_list(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.environments.work.with_raw_response.list( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = response.parse() assert_matches_type(AsyncPageCursor[BetaSelfHostedWork], work, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_streaming_response_list(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.environments.work.with_streaming_response.list( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = await response.parse() assert_matches_type(AsyncPageCursor[BetaSelfHostedWork], work, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_path_params_list(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"): await async_client.beta.environments.work.with_raw_response.list( environment_id="", ) @parametrize async def test_method_ack(self, async_client: AsyncAnthropic) -> None: work = await async_client.beta.environments.work.ack( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert_matches_type(BetaSelfHostedWork, work, path=["response"]) @parametrize async def test_method_ack_with_all_params(self, async_client: AsyncAnthropic) -> None: work = await async_client.beta.environments.work.ack( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaSelfHostedWork, work, path=["response"]) @parametrize async def test_raw_response_ack(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.environments.work.with_raw_response.ack( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = response.parse() assert_matches_type(BetaSelfHostedWork, work, path=["response"]) @parametrize async def test_streaming_response_ack(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.environments.work.with_streaming_response.ack( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = await response.parse() assert_matches_type(BetaSelfHostedWork, work, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_ack(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"): await async_client.beta.environments.work.with_raw_response.ack( work_id="work_id", environment_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `work_id` but received ''"): await async_client.beta.environments.work.with_raw_response.ack( work_id="", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) @parametrize async def test_method_heartbeat(self, async_client: AsyncAnthropic) -> None: work = await async_client.beta.environments.work.heartbeat( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert_matches_type(BetaSelfHostedWorkHeartbeatResponse, work, path=["response"]) @parametrize async def test_method_heartbeat_with_all_params(self, async_client: AsyncAnthropic) -> None: work = await async_client.beta.environments.work.heartbeat( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", desired_ttl_seconds=0, expected_last_heartbeat="expected_last_heartbeat", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaSelfHostedWorkHeartbeatResponse, work, path=["response"]) @parametrize async def test_raw_response_heartbeat(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.environments.work.with_raw_response.heartbeat( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = response.parse() assert_matches_type(BetaSelfHostedWorkHeartbeatResponse, work, path=["response"]) @parametrize async def test_streaming_response_heartbeat(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.environments.work.with_streaming_response.heartbeat( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = await response.parse() assert_matches_type(BetaSelfHostedWorkHeartbeatResponse, work, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_heartbeat(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"): await async_client.beta.environments.work.with_raw_response.heartbeat( work_id="work_id", environment_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `work_id` but received ''"): await async_client.beta.environments.work.with_raw_response.heartbeat( work_id="", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) @parametrize async def test_method_poll(self, async_client: AsyncAnthropic) -> None: work = await async_client.beta.environments.work.poll( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert_matches_type(Optional[BetaSelfHostedWork], work, path=["response"]) @parametrize async def test_method_poll_with_all_params(self, async_client: AsyncAnthropic) -> None: work = await async_client.beta.environments.work.poll( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", block_ms=1, reclaim_older_than_ms=1, betas=["message-batches-2024-09-24"], anthropic_worker_id="Anthropic-Worker-ID", ) assert_matches_type(Optional[BetaSelfHostedWork], work, path=["response"]) @parametrize async def test_raw_response_poll(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.environments.work.with_raw_response.poll( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = response.parse() assert_matches_type(Optional[BetaSelfHostedWork], work, path=["response"]) @parametrize async def test_streaming_response_poll(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.environments.work.with_streaming_response.poll( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = await response.parse() assert_matches_type(Optional[BetaSelfHostedWork], work, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_poll(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"): await async_client.beta.environments.work.with_raw_response.poll( environment_id="", ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_stats(self, async_client: AsyncAnthropic) -> None: work = await async_client.beta.environments.work.stats( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert_matches_type(BetaSelfHostedWorkQueueStats, work, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_stats_with_all_params(self, async_client: AsyncAnthropic) -> None: work = await async_client.beta.environments.work.stats( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaSelfHostedWorkQueueStats, work, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_raw_response_stats(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.environments.work.with_raw_response.stats( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = response.parse() assert_matches_type(BetaSelfHostedWorkQueueStats, work, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_streaming_response_stats(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.environments.work.with_streaming_response.stats( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = await response.parse() assert_matches_type(BetaSelfHostedWorkQueueStats, work, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_path_params_stats(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"): await async_client.beta.environments.work.with_raw_response.stats( environment_id="", ) @parametrize async def test_method_stop(self, async_client: AsyncAnthropic) -> None: work = await async_client.beta.environments.work.stop( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert_matches_type(BetaSelfHostedWork, work, path=["response"]) @parametrize async def test_method_stop_with_all_params(self, async_client: AsyncAnthropic) -> None: work = await async_client.beta.environments.work.stop( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", force=True, betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaSelfHostedWork, work, path=["response"]) @parametrize async def test_raw_response_stop(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.environments.work.with_raw_response.stop( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = response.parse() assert_matches_type(BetaSelfHostedWork, work, path=["response"]) @parametrize async def test_streaming_response_stop(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.environments.work.with_streaming_response.stop( work_id="work_id", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" work = await response.parse() assert_matches_type(BetaSelfHostedWork, work, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_stop(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"): await async_client.beta.environments.work.with_raw_response.stop( work_id="work_id", environment_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `work_id` but received ''"): await async_client.beta.environments.work.with_raw_response.stop( work_id="", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) anthropic-sdk-python-0.120.2/tests/api_resources/beta/memory_stores/000077500000000000000000000000001523216435200255735ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/api_resources/beta/memory_stores/__init__.py000066400000000000000000000001261523216435200277030ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. anthropic-sdk-python-0.120.2/tests/api_resources/beta/memory_stores/test_memories.py000066400000000000000000000634701523216435200310360ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os from typing import Any, cast import pytest from anthropic import Anthropic, AsyncAnthropic from tests.utils import assert_matches_type from anthropic.pagination import SyncPageCursor, AsyncPageCursor from anthropic.types.beta.memory_stores import ( BetaManagedAgentsMemory, BetaManagedAgentsDeletedMemory, BetaManagedAgentsMemoryListItem, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestMemories: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create(self, client: Anthropic) -> None: memory = client.beta.memory_stores.memories.create( memory_store_id="memory_store_id", content="content", path="xx", ) assert_matches_type(BetaManagedAgentsMemory, memory, path=["response"]) @parametrize def test_method_create_with_all_params(self, client: Anthropic) -> None: memory = client.beta.memory_stores.memories.create( memory_store_id="memory_store_id", content="content", path="xx", view="basic", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsMemory, memory, path=["response"]) @parametrize def test_raw_response_create(self, client: Anthropic) -> None: response = client.beta.memory_stores.memories.with_raw_response.create( memory_store_id="memory_store_id", content="content", path="xx", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory = response.parse() assert_matches_type(BetaManagedAgentsMemory, memory, path=["response"]) @parametrize def test_streaming_response_create(self, client: Anthropic) -> None: with client.beta.memory_stores.memories.with_streaming_response.create( memory_store_id="memory_store_id", content="content", path="xx", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory = response.parse() assert_matches_type(BetaManagedAgentsMemory, memory, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_create(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_store_id` but received ''"): client.beta.memory_stores.memories.with_raw_response.create( memory_store_id="", content="content", path="xx", ) @parametrize def test_method_retrieve(self, client: Anthropic) -> None: memory = client.beta.memory_stores.memories.retrieve( memory_id="memory_id", memory_store_id="memory_store_id", ) assert_matches_type(BetaManagedAgentsMemory, memory, path=["response"]) @parametrize def test_method_retrieve_with_all_params(self, client: Anthropic) -> None: memory = client.beta.memory_stores.memories.retrieve( memory_id="memory_id", memory_store_id="memory_store_id", view="basic", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsMemory, memory, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: Anthropic) -> None: response = client.beta.memory_stores.memories.with_raw_response.retrieve( memory_id="memory_id", memory_store_id="memory_store_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory = response.parse() assert_matches_type(BetaManagedAgentsMemory, memory, path=["response"]) @parametrize def test_streaming_response_retrieve(self, client: Anthropic) -> None: with client.beta.memory_stores.memories.with_streaming_response.retrieve( memory_id="memory_id", memory_store_id="memory_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory = response.parse() assert_matches_type(BetaManagedAgentsMemory, memory, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_retrieve(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_store_id` but received ''"): client.beta.memory_stores.memories.with_raw_response.retrieve( memory_id="memory_id", memory_store_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_id` but received ''"): client.beta.memory_stores.memories.with_raw_response.retrieve( memory_id="", memory_store_id="memory_store_id", ) @parametrize def test_method_update(self, client: Anthropic) -> None: memory = client.beta.memory_stores.memories.update( memory_id="memory_id", memory_store_id="memory_store_id", ) assert_matches_type(BetaManagedAgentsMemory, memory, path=["response"]) @parametrize def test_method_update_with_all_params(self, client: Anthropic) -> None: memory = client.beta.memory_stores.memories.update( memory_id="memory_id", memory_store_id="memory_store_id", view="basic", content="content", path="xx", precondition={ "type": "content_sha256", "content_sha256": "content_sha256", }, betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsMemory, memory, path=["response"]) @parametrize def test_raw_response_update(self, client: Anthropic) -> None: response = client.beta.memory_stores.memories.with_raw_response.update( memory_id="memory_id", memory_store_id="memory_store_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory = response.parse() assert_matches_type(BetaManagedAgentsMemory, memory, path=["response"]) @parametrize def test_streaming_response_update(self, client: Anthropic) -> None: with client.beta.memory_stores.memories.with_streaming_response.update( memory_id="memory_id", memory_store_id="memory_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory = response.parse() assert_matches_type(BetaManagedAgentsMemory, memory, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_update(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_store_id` but received ''"): client.beta.memory_stores.memories.with_raw_response.update( memory_id="memory_id", memory_store_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_id` but received ''"): client.beta.memory_stores.memories.with_raw_response.update( memory_id="", memory_store_id="memory_store_id", ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list(self, client: Anthropic) -> None: memory = client.beta.memory_stores.memories.list( memory_store_id="memory_store_id", ) assert_matches_type(SyncPageCursor[BetaManagedAgentsMemoryListItem], memory, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list_with_all_params(self, client: Anthropic) -> None: memory = client.beta.memory_stores.memories.list( memory_store_id="memory_store_id", depth=0, limit=0, page="page", path_prefix="path_prefix", view="basic", betas=["message-batches-2024-09-24"], ) assert_matches_type(SyncPageCursor[BetaManagedAgentsMemoryListItem], memory, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_raw_response_list(self, client: Anthropic) -> None: response = client.beta.memory_stores.memories.with_raw_response.list( memory_store_id="memory_store_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory = response.parse() assert_matches_type(SyncPageCursor[BetaManagedAgentsMemoryListItem], memory, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_streaming_response_list(self, client: Anthropic) -> None: with client.beta.memory_stores.memories.with_streaming_response.list( memory_store_id="memory_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory = response.parse() assert_matches_type(SyncPageCursor[BetaManagedAgentsMemoryListItem], memory, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_path_params_list(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_store_id` but received ''"): client.beta.memory_stores.memories.with_raw_response.list( memory_store_id="", ) @parametrize def test_method_delete(self, client: Anthropic) -> None: memory = client.beta.memory_stores.memories.delete( memory_id="memory_id", memory_store_id="memory_store_id", ) assert_matches_type(BetaManagedAgentsDeletedMemory, memory, path=["response"]) @parametrize def test_method_delete_with_all_params(self, client: Anthropic) -> None: memory = client.beta.memory_stores.memories.delete( memory_id="memory_id", memory_store_id="memory_store_id", expected_content_sha256="expected_content_sha256", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsDeletedMemory, memory, path=["response"]) @parametrize def test_raw_response_delete(self, client: Anthropic) -> None: response = client.beta.memory_stores.memories.with_raw_response.delete( memory_id="memory_id", memory_store_id="memory_store_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory = response.parse() assert_matches_type(BetaManagedAgentsDeletedMemory, memory, path=["response"]) @parametrize def test_streaming_response_delete(self, client: Anthropic) -> None: with client.beta.memory_stores.memories.with_streaming_response.delete( memory_id="memory_id", memory_store_id="memory_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory = response.parse() assert_matches_type(BetaManagedAgentsDeletedMemory, memory, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_delete(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_store_id` but received ''"): client.beta.memory_stores.memories.with_raw_response.delete( memory_id="memory_id", memory_store_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_id` but received ''"): client.beta.memory_stores.memories.with_raw_response.delete( memory_id="", memory_store_id="memory_store_id", ) class TestAsyncMemories: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize async def test_method_create(self, async_client: AsyncAnthropic) -> None: memory = await async_client.beta.memory_stores.memories.create( memory_store_id="memory_store_id", content="content", path="xx", ) assert_matches_type(BetaManagedAgentsMemory, memory, path=["response"]) @parametrize async def test_method_create_with_all_params(self, async_client: AsyncAnthropic) -> None: memory = await async_client.beta.memory_stores.memories.create( memory_store_id="memory_store_id", content="content", path="xx", view="basic", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsMemory, memory, path=["response"]) @parametrize async def test_raw_response_create(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.memory_stores.memories.with_raw_response.create( memory_store_id="memory_store_id", content="content", path="xx", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory = response.parse() assert_matches_type(BetaManagedAgentsMemory, memory, path=["response"]) @parametrize async def test_streaming_response_create(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.memory_stores.memories.with_streaming_response.create( memory_store_id="memory_store_id", content="content", path="xx", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory = await response.parse() assert_matches_type(BetaManagedAgentsMemory, memory, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_create(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_store_id` but received ''"): await async_client.beta.memory_stores.memories.with_raw_response.create( memory_store_id="", content="content", path="xx", ) @parametrize async def test_method_retrieve(self, async_client: AsyncAnthropic) -> None: memory = await async_client.beta.memory_stores.memories.retrieve( memory_id="memory_id", memory_store_id="memory_store_id", ) assert_matches_type(BetaManagedAgentsMemory, memory, path=["response"]) @parametrize async def test_method_retrieve_with_all_params(self, async_client: AsyncAnthropic) -> None: memory = await async_client.beta.memory_stores.memories.retrieve( memory_id="memory_id", memory_store_id="memory_store_id", view="basic", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsMemory, memory, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.memory_stores.memories.with_raw_response.retrieve( memory_id="memory_id", memory_store_id="memory_store_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory = response.parse() assert_matches_type(BetaManagedAgentsMemory, memory, path=["response"]) @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.memory_stores.memories.with_streaming_response.retrieve( memory_id="memory_id", memory_store_id="memory_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory = await response.parse() assert_matches_type(BetaManagedAgentsMemory, memory, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_retrieve(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_store_id` but received ''"): await async_client.beta.memory_stores.memories.with_raw_response.retrieve( memory_id="memory_id", memory_store_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_id` but received ''"): await async_client.beta.memory_stores.memories.with_raw_response.retrieve( memory_id="", memory_store_id="memory_store_id", ) @parametrize async def test_method_update(self, async_client: AsyncAnthropic) -> None: memory = await async_client.beta.memory_stores.memories.update( memory_id="memory_id", memory_store_id="memory_store_id", ) assert_matches_type(BetaManagedAgentsMemory, memory, path=["response"]) @parametrize async def test_method_update_with_all_params(self, async_client: AsyncAnthropic) -> None: memory = await async_client.beta.memory_stores.memories.update( memory_id="memory_id", memory_store_id="memory_store_id", view="basic", content="content", path="xx", precondition={ "type": "content_sha256", "content_sha256": "content_sha256", }, betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsMemory, memory, path=["response"]) @parametrize async def test_raw_response_update(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.memory_stores.memories.with_raw_response.update( memory_id="memory_id", memory_store_id="memory_store_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory = response.parse() assert_matches_type(BetaManagedAgentsMemory, memory, path=["response"]) @parametrize async def test_streaming_response_update(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.memory_stores.memories.with_streaming_response.update( memory_id="memory_id", memory_store_id="memory_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory = await response.parse() assert_matches_type(BetaManagedAgentsMemory, memory, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_update(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_store_id` but received ''"): await async_client.beta.memory_stores.memories.with_raw_response.update( memory_id="memory_id", memory_store_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_id` but received ''"): await async_client.beta.memory_stores.memories.with_raw_response.update( memory_id="", memory_store_id="memory_store_id", ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list(self, async_client: AsyncAnthropic) -> None: memory = await async_client.beta.memory_stores.memories.list( memory_store_id="memory_store_id", ) assert_matches_type(AsyncPageCursor[BetaManagedAgentsMemoryListItem], memory, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncAnthropic) -> None: memory = await async_client.beta.memory_stores.memories.list( memory_store_id="memory_store_id", depth=0, limit=0, page="page", path_prefix="path_prefix", view="basic", betas=["message-batches-2024-09-24"], ) assert_matches_type(AsyncPageCursor[BetaManagedAgentsMemoryListItem], memory, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_raw_response_list(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.memory_stores.memories.with_raw_response.list( memory_store_id="memory_store_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory = response.parse() assert_matches_type(AsyncPageCursor[BetaManagedAgentsMemoryListItem], memory, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_streaming_response_list(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.memory_stores.memories.with_streaming_response.list( memory_store_id="memory_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory = await response.parse() assert_matches_type(AsyncPageCursor[BetaManagedAgentsMemoryListItem], memory, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_path_params_list(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_store_id` but received ''"): await async_client.beta.memory_stores.memories.with_raw_response.list( memory_store_id="", ) @parametrize async def test_method_delete(self, async_client: AsyncAnthropic) -> None: memory = await async_client.beta.memory_stores.memories.delete( memory_id="memory_id", memory_store_id="memory_store_id", ) assert_matches_type(BetaManagedAgentsDeletedMemory, memory, path=["response"]) @parametrize async def test_method_delete_with_all_params(self, async_client: AsyncAnthropic) -> None: memory = await async_client.beta.memory_stores.memories.delete( memory_id="memory_id", memory_store_id="memory_store_id", expected_content_sha256="expected_content_sha256", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsDeletedMemory, memory, path=["response"]) @parametrize async def test_raw_response_delete(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.memory_stores.memories.with_raw_response.delete( memory_id="memory_id", memory_store_id="memory_store_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory = response.parse() assert_matches_type(BetaManagedAgentsDeletedMemory, memory, path=["response"]) @parametrize async def test_streaming_response_delete(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.memory_stores.memories.with_streaming_response.delete( memory_id="memory_id", memory_store_id="memory_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory = await response.parse() assert_matches_type(BetaManagedAgentsDeletedMemory, memory, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_delete(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_store_id` but received ''"): await async_client.beta.memory_stores.memories.with_raw_response.delete( memory_id="memory_id", memory_store_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_id` but received ''"): await async_client.beta.memory_stores.memories.with_raw_response.delete( memory_id="", memory_store_id="memory_store_id", ) anthropic-sdk-python-0.120.2/tests/api_resources/beta/memory_stores/test_memory_versions.py000066400000000000000000000425161523216435200324540ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os from typing import Any, cast import pytest from anthropic import Anthropic, AsyncAnthropic from tests.utils import assert_matches_type from anthropic._utils import parse_datetime from anthropic.pagination import SyncPageCursor, AsyncPageCursor from anthropic.types.beta.memory_stores import ( BetaManagedAgentsMemoryVersion, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestMemoryVersions: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_retrieve(self, client: Anthropic) -> None: memory_version = client.beta.memory_stores.memory_versions.retrieve( memory_version_id="memory_version_id", memory_store_id="memory_store_id", ) assert_matches_type(BetaManagedAgentsMemoryVersion, memory_version, path=["response"]) @parametrize def test_method_retrieve_with_all_params(self, client: Anthropic) -> None: memory_version = client.beta.memory_stores.memory_versions.retrieve( memory_version_id="memory_version_id", memory_store_id="memory_store_id", view="basic", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsMemoryVersion, memory_version, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: Anthropic) -> None: response = client.beta.memory_stores.memory_versions.with_raw_response.retrieve( memory_version_id="memory_version_id", memory_store_id="memory_store_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_version = response.parse() assert_matches_type(BetaManagedAgentsMemoryVersion, memory_version, path=["response"]) @parametrize def test_streaming_response_retrieve(self, client: Anthropic) -> None: with client.beta.memory_stores.memory_versions.with_streaming_response.retrieve( memory_version_id="memory_version_id", memory_store_id="memory_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_version = response.parse() assert_matches_type(BetaManagedAgentsMemoryVersion, memory_version, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_retrieve(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_store_id` but received ''"): client.beta.memory_stores.memory_versions.with_raw_response.retrieve( memory_version_id="memory_version_id", memory_store_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_version_id` but received ''"): client.beta.memory_stores.memory_versions.with_raw_response.retrieve( memory_version_id="", memory_store_id="memory_store_id", ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list(self, client: Anthropic) -> None: memory_version = client.beta.memory_stores.memory_versions.list( memory_store_id="memory_store_id", ) assert_matches_type(SyncPageCursor[BetaManagedAgentsMemoryVersion], memory_version, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list_with_all_params(self, client: Anthropic) -> None: memory_version = client.beta.memory_stores.memory_versions.list( memory_store_id="memory_store_id", api_key_id="api_key_id", created_at_gte=parse_datetime("2019-12-27T18:11:19.117Z"), created_at_lte=parse_datetime("2019-12-27T18:11:19.117Z"), limit=0, memory_id="memory_id", operation="created", page="page", session_id="session_id", view="basic", betas=["message-batches-2024-09-24"], ) assert_matches_type(SyncPageCursor[BetaManagedAgentsMemoryVersion], memory_version, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_raw_response_list(self, client: Anthropic) -> None: response = client.beta.memory_stores.memory_versions.with_raw_response.list( memory_store_id="memory_store_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_version = response.parse() assert_matches_type(SyncPageCursor[BetaManagedAgentsMemoryVersion], memory_version, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_streaming_response_list(self, client: Anthropic) -> None: with client.beta.memory_stores.memory_versions.with_streaming_response.list( memory_store_id="memory_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_version = response.parse() assert_matches_type(SyncPageCursor[BetaManagedAgentsMemoryVersion], memory_version, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_path_params_list(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_store_id` but received ''"): client.beta.memory_stores.memory_versions.with_raw_response.list( memory_store_id="", ) @parametrize def test_method_redact(self, client: Anthropic) -> None: memory_version = client.beta.memory_stores.memory_versions.redact( memory_version_id="memory_version_id", memory_store_id="memory_store_id", ) assert_matches_type(BetaManagedAgentsMemoryVersion, memory_version, path=["response"]) @parametrize def test_method_redact_with_all_params(self, client: Anthropic) -> None: memory_version = client.beta.memory_stores.memory_versions.redact( memory_version_id="memory_version_id", memory_store_id="memory_store_id", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsMemoryVersion, memory_version, path=["response"]) @parametrize def test_raw_response_redact(self, client: Anthropic) -> None: response = client.beta.memory_stores.memory_versions.with_raw_response.redact( memory_version_id="memory_version_id", memory_store_id="memory_store_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_version = response.parse() assert_matches_type(BetaManagedAgentsMemoryVersion, memory_version, path=["response"]) @parametrize def test_streaming_response_redact(self, client: Anthropic) -> None: with client.beta.memory_stores.memory_versions.with_streaming_response.redact( memory_version_id="memory_version_id", memory_store_id="memory_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_version = response.parse() assert_matches_type(BetaManagedAgentsMemoryVersion, memory_version, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_redact(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_store_id` but received ''"): client.beta.memory_stores.memory_versions.with_raw_response.redact( memory_version_id="memory_version_id", memory_store_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_version_id` but received ''"): client.beta.memory_stores.memory_versions.with_raw_response.redact( memory_version_id="", memory_store_id="memory_store_id", ) class TestAsyncMemoryVersions: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize async def test_method_retrieve(self, async_client: AsyncAnthropic) -> None: memory_version = await async_client.beta.memory_stores.memory_versions.retrieve( memory_version_id="memory_version_id", memory_store_id="memory_store_id", ) assert_matches_type(BetaManagedAgentsMemoryVersion, memory_version, path=["response"]) @parametrize async def test_method_retrieve_with_all_params(self, async_client: AsyncAnthropic) -> None: memory_version = await async_client.beta.memory_stores.memory_versions.retrieve( memory_version_id="memory_version_id", memory_store_id="memory_store_id", view="basic", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsMemoryVersion, memory_version, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.memory_stores.memory_versions.with_raw_response.retrieve( memory_version_id="memory_version_id", memory_store_id="memory_store_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_version = response.parse() assert_matches_type(BetaManagedAgentsMemoryVersion, memory_version, path=["response"]) @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.memory_stores.memory_versions.with_streaming_response.retrieve( memory_version_id="memory_version_id", memory_store_id="memory_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_version = await response.parse() assert_matches_type(BetaManagedAgentsMemoryVersion, memory_version, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_retrieve(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_store_id` but received ''"): await async_client.beta.memory_stores.memory_versions.with_raw_response.retrieve( memory_version_id="memory_version_id", memory_store_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_version_id` but received ''"): await async_client.beta.memory_stores.memory_versions.with_raw_response.retrieve( memory_version_id="", memory_store_id="memory_store_id", ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list(self, async_client: AsyncAnthropic) -> None: memory_version = await async_client.beta.memory_stores.memory_versions.list( memory_store_id="memory_store_id", ) assert_matches_type(AsyncPageCursor[BetaManagedAgentsMemoryVersion], memory_version, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncAnthropic) -> None: memory_version = await async_client.beta.memory_stores.memory_versions.list( memory_store_id="memory_store_id", api_key_id="api_key_id", created_at_gte=parse_datetime("2019-12-27T18:11:19.117Z"), created_at_lte=parse_datetime("2019-12-27T18:11:19.117Z"), limit=0, memory_id="memory_id", operation="created", page="page", session_id="session_id", view="basic", betas=["message-batches-2024-09-24"], ) assert_matches_type(AsyncPageCursor[BetaManagedAgentsMemoryVersion], memory_version, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_raw_response_list(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.memory_stores.memory_versions.with_raw_response.list( memory_store_id="memory_store_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_version = response.parse() assert_matches_type(AsyncPageCursor[BetaManagedAgentsMemoryVersion], memory_version, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_streaming_response_list(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.memory_stores.memory_versions.with_streaming_response.list( memory_store_id="memory_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_version = await response.parse() assert_matches_type(AsyncPageCursor[BetaManagedAgentsMemoryVersion], memory_version, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_path_params_list(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_store_id` but received ''"): await async_client.beta.memory_stores.memory_versions.with_raw_response.list( memory_store_id="", ) @parametrize async def test_method_redact(self, async_client: AsyncAnthropic) -> None: memory_version = await async_client.beta.memory_stores.memory_versions.redact( memory_version_id="memory_version_id", memory_store_id="memory_store_id", ) assert_matches_type(BetaManagedAgentsMemoryVersion, memory_version, path=["response"]) @parametrize async def test_method_redact_with_all_params(self, async_client: AsyncAnthropic) -> None: memory_version = await async_client.beta.memory_stores.memory_versions.redact( memory_version_id="memory_version_id", memory_store_id="memory_store_id", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsMemoryVersion, memory_version, path=["response"]) @parametrize async def test_raw_response_redact(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.memory_stores.memory_versions.with_raw_response.redact( memory_version_id="memory_version_id", memory_store_id="memory_store_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_version = response.parse() assert_matches_type(BetaManagedAgentsMemoryVersion, memory_version, path=["response"]) @parametrize async def test_streaming_response_redact(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.memory_stores.memory_versions.with_streaming_response.redact( memory_version_id="memory_version_id", memory_store_id="memory_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_version = await response.parse() assert_matches_type(BetaManagedAgentsMemoryVersion, memory_version, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_redact(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_store_id` but received ''"): await async_client.beta.memory_stores.memory_versions.with_raw_response.redact( memory_version_id="memory_version_id", memory_store_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_version_id` but received ''"): await async_client.beta.memory_stores.memory_versions.with_raw_response.redact( memory_version_id="", memory_store_id="memory_store_id", ) anthropic-sdk-python-0.120.2/tests/api_resources/beta/messages/000077500000000000000000000000001523216435200244735ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/api_resources/beta/messages/__init__.py000066400000000000000000000001261523216435200266030ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. anthropic-sdk-python-0.120.2/tests/api_resources/beta/messages/test_batches.py000066400000000000000000001156111523216435200275220ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os import json from typing import Any, cast import httpx import pytest from respx import MockRouter from anthropic import Anthropic, AsyncAnthropic from tests.utils import assert_matches_type from anthropic.pagination import SyncPage, AsyncPage from anthropic.types.beta.messages import ( BetaMessageBatch, BetaDeletedMessageBatch, BetaMessageBatchIndividualResponse, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestBatches: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create(self, client: Anthropic) -> None: batch = client.beta.messages.batches.create( requests=[ { "custom_id": "my-custom-id-1", "params": { "max_tokens": 1024, "messages": [ { "content": "Hello, world", "role": "user", } ], "model": "claude-opus-4-6", }, } ], ) assert_matches_type(BetaMessageBatch, batch, path=["response"]) @parametrize def test_method_create_with_all_params(self, client: Anthropic) -> None: batch = client.beta.messages.batches.create( requests=[ { "custom_id": "my-custom-id-1", "params": { "max_tokens": 1024, "messages": [ { "content": "Hello, world", "role": "user", } ], "model": "claude-opus-4-6", "cache_control": { "type": "ephemeral", "ttl": "5m", }, "container": { "id": "id", "skills": [ { "skill_id": "pdf", "type": "anthropic", "version": "latest", } ], }, "context_management": { "edits": [ { "type": "clear_tool_uses_20250919", "clear_at_least": { "type": "input_tokens", "value": 0, }, "clear_tool_inputs": True, "exclude_tools": ["string"], "keep": { "type": "tool_uses", "value": 0, }, "trigger": { "type": "input_tokens", "value": 1, }, } ] }, "diagnostics": {"previous_message_id": "previous_message_id"}, "fallback_credit_token": "x", "fallbacks": "default", "inference_geo": "inference_geo", "mcp_servers": [ { "name": "name", "type": "url", "url": "url", "authorization_token": "authorization_token", "tool_configuration": { "allowed_tools": ["string"], "enabled": True, }, } ], "metadata": {"user_id": "13803d75-b4b5-4c3e-b2a2-6f21399b021b"}, "output_config": { "effort": "low", "format": { "schema": {"foo": "bar"}, "type": "json_schema", }, "task_budget": { "total": 1024, "type": "tokens", "remaining": 0, }, }, "output_format": { "schema": {"foo": "bar"}, "type": "json_schema", }, "service_tier": "auto", "speed": "standard", "stop_sequences": ["string"], "stream": False, "system": [ { "text": "Today's date is 2024-06-01.", "type": "text", "cache_control": { "type": "ephemeral", "ttl": "5m", }, "citations": [ { "cited_text": "The grass is green. The sky is blue.", "document_index": 0, "document_title": "x", "end_char_index": 0, "start_char_index": 0, "type": "char_location", } ], } ], "temperature": 1, "thinking": { "type": "adaptive", "display": "summarized", }, "tool_choice": { "type": "auto", "disable_parallel_tool_use": True, }, "tools": [ { "input_schema": { "type": "object", "properties": { "location": "bar", "unit": "bar", }, "required": ["location"], }, "name": "name", "allowed_callers": ["direct"], "cache_control": { "type": "ephemeral", "ttl": "5m", }, "defer_loading": True, "description": "Get the current weather in a given location", "eager_input_streaming": True, "input_examples": [{"foo": "bar"}], "strict": True, "type": "custom", } ], "top_k": 5, "top_p": 0.7, }, } ], betas=["string"], user_profile_id="anthropic-user-profile-id", ) assert_matches_type(BetaMessageBatch, batch, path=["response"]) @parametrize def test_raw_response_create(self, client: Anthropic) -> None: response = client.beta.messages.batches.with_raw_response.create( requests=[ { "custom_id": "my-custom-id-1", "params": { "max_tokens": 1024, "messages": [ { "content": "Hello, world", "role": "user", } ], "model": "claude-opus-4-6", }, } ], ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(BetaMessageBatch, batch, path=["response"]) @parametrize def test_streaming_response_create(self, client: Anthropic) -> None: with client.beta.messages.batches.with_streaming_response.create( requests=[ { "custom_id": "my-custom-id-1", "params": { "max_tokens": 1024, "messages": [ { "content": "Hello, world", "role": "user", } ], "model": "claude-opus-4-6", }, } ], ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(BetaMessageBatch, batch, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_retrieve(self, client: Anthropic) -> None: batch = client.beta.messages.batches.retrieve( message_batch_id="message_batch_id", ) assert_matches_type(BetaMessageBatch, batch, path=["response"]) @parametrize def test_method_retrieve_with_all_params(self, client: Anthropic) -> None: batch = client.beta.messages.batches.retrieve( message_batch_id="message_batch_id", betas=["string"], ) assert_matches_type(BetaMessageBatch, batch, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: Anthropic) -> None: response = client.beta.messages.batches.with_raw_response.retrieve( message_batch_id="message_batch_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(BetaMessageBatch, batch, path=["response"]) @parametrize def test_streaming_response_retrieve(self, client: Anthropic) -> None: with client.beta.messages.batches.with_streaming_response.retrieve( message_batch_id="message_batch_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(BetaMessageBatch, batch, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_retrieve(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_batch_id` but received ''"): client.beta.messages.batches.with_raw_response.retrieve( message_batch_id="", ) @parametrize def test_method_list(self, client: Anthropic) -> None: batch = client.beta.messages.batches.list() assert_matches_type(SyncPage[BetaMessageBatch], batch, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: Anthropic) -> None: batch = client.beta.messages.batches.list( after_id="after_id", before_id="before_id", limit=1, betas=["string"], ) assert_matches_type(SyncPage[BetaMessageBatch], batch, path=["response"]) @parametrize def test_raw_response_list(self, client: Anthropic) -> None: response = client.beta.messages.batches.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(SyncPage[BetaMessageBatch], batch, path=["response"]) @parametrize def test_streaming_response_list(self, client: Anthropic) -> None: with client.beta.messages.batches.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(SyncPage[BetaMessageBatch], batch, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_delete(self, client: Anthropic) -> None: batch = client.beta.messages.batches.delete( message_batch_id="message_batch_id", ) assert_matches_type(BetaDeletedMessageBatch, batch, path=["response"]) @parametrize def test_method_delete_with_all_params(self, client: Anthropic) -> None: batch = client.beta.messages.batches.delete( message_batch_id="message_batch_id", betas=["string"], ) assert_matches_type(BetaDeletedMessageBatch, batch, path=["response"]) @parametrize def test_raw_response_delete(self, client: Anthropic) -> None: response = client.beta.messages.batches.with_raw_response.delete( message_batch_id="message_batch_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(BetaDeletedMessageBatch, batch, path=["response"]) @parametrize def test_streaming_response_delete(self, client: Anthropic) -> None: with client.beta.messages.batches.with_streaming_response.delete( message_batch_id="message_batch_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(BetaDeletedMessageBatch, batch, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_delete(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_batch_id` but received ''"): client.beta.messages.batches.with_raw_response.delete( message_batch_id="", ) @parametrize def test_method_cancel(self, client: Anthropic) -> None: batch = client.beta.messages.batches.cancel( message_batch_id="message_batch_id", ) assert_matches_type(BetaMessageBatch, batch, path=["response"]) @parametrize def test_method_cancel_with_all_params(self, client: Anthropic) -> None: batch = client.beta.messages.batches.cancel( message_batch_id="message_batch_id", betas=["string"], ) assert_matches_type(BetaMessageBatch, batch, path=["response"]) @parametrize def test_raw_response_cancel(self, client: Anthropic) -> None: response = client.beta.messages.batches.with_raw_response.cancel( message_batch_id="message_batch_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(BetaMessageBatch, batch, path=["response"]) @parametrize def test_streaming_response_cancel(self, client: Anthropic) -> None: with client.beta.messages.batches.with_streaming_response.cancel( message_batch_id="message_batch_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(BetaMessageBatch, batch, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_cancel(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_batch_id` but received ''"): client.beta.messages.batches.with_raw_response.cancel( message_batch_id="", ) @pytest.mark.respx(base_url=base_url) @pytest.mark.parametrize("client", [False], indirect=True) def test_method_results(self, client: Anthropic, respx_mock: MockRouter) -> None: respx_mock.get("/v1/messages/batches/message_batch_id?beta=true").mock( return_value=httpx.Response( 200, json={"results_url": "/v1/messages/batches/message_batch_id/results?beta=true"} ) ) respx_mock.get("/v1/messages/batches/message_batch_id/results?beta=true").mock( return_value=httpx.Response( 200, content="\n".join([json.dumps({"foo": "bar"}), json.dumps({"bar": "baz"})]) ) ) results = client.beta.messages.batches.results( message_batch_id="message_batch_id", ) assert results.http_response is not None assert not results.http_response.is_stream_consumed i = 0 for i, result in enumerate(results): if i == 0: assert result.to_dict() == {"foo": "bar"} elif i == 1: assert result.to_dict() == {"bar": "baz"} else: raise RuntimeError(f"iterated too many times, expected 2 times but got {i + 1}") assert i == 1 assert results.http_response.is_stream_consumed @parametrize def test_path_params_results(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_batch_id` but received ''"): client.beta.messages.batches.results( message_batch_id="", ) @parametrize @pytest.mark.skip(reason="somehow hitting prod endpoint") def test_raw_response_results(self, client: Anthropic) -> None: response = client.beta.messages.batches.with_raw_response.results( message_batch_id="message_batch_id", ) assert response.http_request.headers.get("X-Stainless-Lang") == "python" stream = response.parse() for item in stream: assert_matches_type(BetaMessageBatchIndividualResponse, item, path=["line"]) @parametrize @pytest.mark.skip(reason="somehow hitting prod endpoint") def test_streaming_response_results(self, client: Anthropic) -> None: with client.beta.messages.batches.with_streaming_response.results( message_batch_id="message_batch_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" stream = response.parse() for item in stream: assert_matches_type(BetaMessageBatchIndividualResponse, item, path=["item"]) assert cast(Any, response.is_closed) is True class TestAsyncBatches: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize async def test_method_create(self, async_client: AsyncAnthropic) -> None: batch = await async_client.beta.messages.batches.create( requests=[ { "custom_id": "my-custom-id-1", "params": { "max_tokens": 1024, "messages": [ { "content": "Hello, world", "role": "user", } ], "model": "claude-opus-4-6", }, } ], ) assert_matches_type(BetaMessageBatch, batch, path=["response"]) @parametrize async def test_method_create_with_all_params(self, async_client: AsyncAnthropic) -> None: batch = await async_client.beta.messages.batches.create( requests=[ { "custom_id": "my-custom-id-1", "params": { "max_tokens": 1024, "messages": [ { "content": "Hello, world", "role": "user", } ], "model": "claude-opus-4-6", "cache_control": { "type": "ephemeral", "ttl": "5m", }, "container": { "id": "id", "skills": [ { "skill_id": "pdf", "type": "anthropic", "version": "latest", } ], }, "context_management": { "edits": [ { "type": "clear_tool_uses_20250919", "clear_at_least": { "type": "input_tokens", "value": 0, }, "clear_tool_inputs": True, "exclude_tools": ["string"], "keep": { "type": "tool_uses", "value": 0, }, "trigger": { "type": "input_tokens", "value": 1, }, } ] }, "diagnostics": {"previous_message_id": "previous_message_id"}, "fallback_credit_token": "x", "fallbacks": "default", "inference_geo": "inference_geo", "mcp_servers": [ { "name": "name", "type": "url", "url": "url", "authorization_token": "authorization_token", "tool_configuration": { "allowed_tools": ["string"], "enabled": True, }, } ], "metadata": {"user_id": "13803d75-b4b5-4c3e-b2a2-6f21399b021b"}, "output_config": { "effort": "low", "format": { "schema": {"foo": "bar"}, "type": "json_schema", }, "task_budget": { "total": 1024, "type": "tokens", "remaining": 0, }, }, "output_format": { "schema": {"foo": "bar"}, "type": "json_schema", }, "service_tier": "auto", "speed": "standard", "stop_sequences": ["string"], "stream": False, "system": [ { "text": "Today's date is 2024-06-01.", "type": "text", "cache_control": { "type": "ephemeral", "ttl": "5m", }, "citations": [ { "cited_text": "The grass is green. The sky is blue.", "document_index": 0, "document_title": "x", "end_char_index": 0, "start_char_index": 0, "type": "char_location", } ], } ], "temperature": 1, "thinking": { "type": "adaptive", "display": "summarized", }, "tool_choice": { "type": "auto", "disable_parallel_tool_use": True, }, "tools": [ { "input_schema": { "type": "object", "properties": { "location": "bar", "unit": "bar", }, "required": ["location"], }, "name": "name", "allowed_callers": ["direct"], "cache_control": { "type": "ephemeral", "ttl": "5m", }, "defer_loading": True, "description": "Get the current weather in a given location", "eager_input_streaming": True, "input_examples": [{"foo": "bar"}], "strict": True, "type": "custom", } ], "top_k": 5, "top_p": 0.7, }, } ], betas=["string"], user_profile_id="anthropic-user-profile-id", ) assert_matches_type(BetaMessageBatch, batch, path=["response"]) @parametrize async def test_raw_response_create(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.messages.batches.with_raw_response.create( requests=[ { "custom_id": "my-custom-id-1", "params": { "max_tokens": 1024, "messages": [ { "content": "Hello, world", "role": "user", } ], "model": "claude-opus-4-6", }, } ], ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(BetaMessageBatch, batch, path=["response"]) @parametrize async def test_streaming_response_create(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.messages.batches.with_streaming_response.create( requests=[ { "custom_id": "my-custom-id-1", "params": { "max_tokens": 1024, "messages": [ { "content": "Hello, world", "role": "user", } ], "model": "claude-opus-4-6", }, } ], ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = await response.parse() assert_matches_type(BetaMessageBatch, batch, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_retrieve(self, async_client: AsyncAnthropic) -> None: batch = await async_client.beta.messages.batches.retrieve( message_batch_id="message_batch_id", ) assert_matches_type(BetaMessageBatch, batch, path=["response"]) @parametrize async def test_method_retrieve_with_all_params(self, async_client: AsyncAnthropic) -> None: batch = await async_client.beta.messages.batches.retrieve( message_batch_id="message_batch_id", betas=["string"], ) assert_matches_type(BetaMessageBatch, batch, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.messages.batches.with_raw_response.retrieve( message_batch_id="message_batch_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(BetaMessageBatch, batch, path=["response"]) @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.messages.batches.with_streaming_response.retrieve( message_batch_id="message_batch_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = await response.parse() assert_matches_type(BetaMessageBatch, batch, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_retrieve(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_batch_id` but received ''"): await async_client.beta.messages.batches.with_raw_response.retrieve( message_batch_id="", ) @parametrize async def test_method_list(self, async_client: AsyncAnthropic) -> None: batch = await async_client.beta.messages.batches.list() assert_matches_type(AsyncPage[BetaMessageBatch], batch, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncAnthropic) -> None: batch = await async_client.beta.messages.batches.list( after_id="after_id", before_id="before_id", limit=1, betas=["string"], ) assert_matches_type(AsyncPage[BetaMessageBatch], batch, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.messages.batches.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(AsyncPage[BetaMessageBatch], batch, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.messages.batches.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = await response.parse() assert_matches_type(AsyncPage[BetaMessageBatch], batch, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_delete(self, async_client: AsyncAnthropic) -> None: batch = await async_client.beta.messages.batches.delete( message_batch_id="message_batch_id", ) assert_matches_type(BetaDeletedMessageBatch, batch, path=["response"]) @parametrize async def test_method_delete_with_all_params(self, async_client: AsyncAnthropic) -> None: batch = await async_client.beta.messages.batches.delete( message_batch_id="message_batch_id", betas=["string"], ) assert_matches_type(BetaDeletedMessageBatch, batch, path=["response"]) @parametrize async def test_raw_response_delete(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.messages.batches.with_raw_response.delete( message_batch_id="message_batch_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(BetaDeletedMessageBatch, batch, path=["response"]) @parametrize async def test_streaming_response_delete(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.messages.batches.with_streaming_response.delete( message_batch_id="message_batch_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = await response.parse() assert_matches_type(BetaDeletedMessageBatch, batch, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_delete(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_batch_id` but received ''"): await async_client.beta.messages.batches.with_raw_response.delete( message_batch_id="", ) @parametrize async def test_method_cancel(self, async_client: AsyncAnthropic) -> None: batch = await async_client.beta.messages.batches.cancel( message_batch_id="message_batch_id", ) assert_matches_type(BetaMessageBatch, batch, path=["response"]) @parametrize async def test_method_cancel_with_all_params(self, async_client: AsyncAnthropic) -> None: batch = await async_client.beta.messages.batches.cancel( message_batch_id="message_batch_id", betas=["string"], ) assert_matches_type(BetaMessageBatch, batch, path=["response"]) @parametrize async def test_raw_response_cancel(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.messages.batches.with_raw_response.cancel( message_batch_id="message_batch_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(BetaMessageBatch, batch, path=["response"]) @parametrize async def test_streaming_response_cancel(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.messages.batches.with_streaming_response.cancel( message_batch_id="message_batch_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = await response.parse() assert_matches_type(BetaMessageBatch, batch, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_cancel(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_batch_id` but received ''"): await async_client.beta.messages.batches.with_raw_response.cancel( message_batch_id="", ) @pytest.mark.respx(base_url=base_url) @pytest.mark.parametrize("async_client", [False], indirect=True) async def test_method_results(self, async_client: AsyncAnthropic, respx_mock: MockRouter) -> None: respx_mock.get("/v1/messages/batches/message_batch_id?beta=true").mock( return_value=httpx.Response( 200, json={"results_url": "/v1/messages/batches/message_batch_id/results?beta=true"} ) ) respx_mock.get("/v1/messages/batches/message_batch_id/results?beta=true").mock( return_value=httpx.Response( 200, content="\n".join([json.dumps({"foo": "bar"}), json.dumps({"bar": "baz"})]) ) ) results = await async_client.beta.messages.batches.results( message_batch_id="message_batch_id", ) assert results.http_response is not None assert not results.http_response.is_stream_consumed i = -1 async for result in results: i += 1 if i == 0: assert result.to_dict() == {"foo": "bar"} elif i == 1: assert result.to_dict() == {"bar": "baz"} else: raise RuntimeError(f"iterated too many times, expected 2 times but got {i + 1}") assert i == 1 assert results.http_response.is_stream_consumed @parametrize async def test_path_params_results(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_batch_id` but received ''"): await async_client.beta.messages.batches.results( message_batch_id="", ) @parametrize @pytest.mark.skip(reason="somehow hitting prod endpoint") async def test_raw_response_results(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.messages.batches.with_raw_response.results( message_batch_id="message_batch_id", ) assert response.http_request.headers.get("X-Stainless-Lang") == "python" stream = response.parse() async for item in stream: assert_matches_type(BetaMessageBatchIndividualResponse, item, path=["line"]) anthropic-sdk-python-0.120.2/tests/api_resources/beta/sessions/000077500000000000000000000000001523216435200245325ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/api_resources/beta/sessions/__init__.py000066400000000000000000000001261523216435200266420ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. anthropic-sdk-python-0.120.2/tests/api_resources/beta/sessions/test_events.py000066400000000000000000000423601523216435200274540ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os from typing import Any, cast import pytest from anthropic import Anthropic, AsyncAnthropic from tests.utils import assert_matches_type from anthropic._utils import parse_datetime from anthropic.pagination import SyncPageCursor, AsyncPageCursor from anthropic.types.beta.sessions import ( BetaManagedAgentsSessionEvent, BetaManagedAgentsSendSessionEvents, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestEvents: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list(self, client: Anthropic) -> None: event = client.beta.sessions.events.list( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert_matches_type(SyncPageCursor[BetaManagedAgentsSessionEvent], event, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list_with_all_params(self, client: Anthropic) -> None: event = client.beta.sessions.events.list( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", created_at_gt=parse_datetime("2019-12-27T18:11:19.117Z"), created_at_gte=parse_datetime("2019-12-27T18:11:19.117Z"), created_at_lt=parse_datetime("2019-12-27T18:11:19.117Z"), created_at_lte=parse_datetime("2019-12-27T18:11:19.117Z"), limit=0, order="asc", page="page", types=["string"], betas=["string"], ) assert_matches_type(SyncPageCursor[BetaManagedAgentsSessionEvent], event, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_raw_response_list(self, client: Anthropic) -> None: response = client.beta.sessions.events.with_raw_response.list( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" event = response.parse() assert_matches_type(SyncPageCursor[BetaManagedAgentsSessionEvent], event, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_streaming_response_list(self, client: Anthropic) -> None: with client.beta.sessions.events.with_streaming_response.list( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" event = response.parse() assert_matches_type(SyncPageCursor[BetaManagedAgentsSessionEvent], event, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_path_params_list(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): client.beta.sessions.events.with_raw_response.list( session_id="", ) @parametrize def test_method_send(self, client: Anthropic) -> None: event = client.beta.sessions.events.send( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", events=[ { "content": [ { "text": "Where is my order #1234?", "type": "text", } ], "type": "user.message", } ], ) assert_matches_type(BetaManagedAgentsSendSessionEvents, event, path=["response"]) @parametrize def test_method_send_with_all_params(self, client: Anthropic) -> None: event = client.beta.sessions.events.send( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", events=[ { "content": [ { "text": "Where is my order #1234?", "type": "text", } ], "type": "user.message", } ], betas=["string"], ) assert_matches_type(BetaManagedAgentsSendSessionEvents, event, path=["response"]) @parametrize def test_raw_response_send(self, client: Anthropic) -> None: response = client.beta.sessions.events.with_raw_response.send( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", events=[ { "content": [ { "text": "Where is my order #1234?", "type": "text", } ], "type": "user.message", } ], ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" event = response.parse() assert_matches_type(BetaManagedAgentsSendSessionEvents, event, path=["response"]) @parametrize def test_streaming_response_send(self, client: Anthropic) -> None: with client.beta.sessions.events.with_streaming_response.send( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", events=[ { "content": [ { "text": "Where is my order #1234?", "type": "text", } ], "type": "user.message", } ], ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" event = response.parse() assert_matches_type(BetaManagedAgentsSendSessionEvents, event, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_send(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): client.beta.sessions.events.with_raw_response.send( session_id="", events=[ { "content": [ { "text": "Where is my order #1234?", "type": "text", } ], "type": "user.message", } ], ) @parametrize def test_method_stream(self, client: Anthropic) -> None: event_stream = client.beta.sessions.events.stream( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) event_stream.response.close() @parametrize def test_method_stream_with_all_params(self, client: Anthropic) -> None: event_stream = client.beta.sessions.events.stream( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", event_deltas=["agent.message"], betas=["string"], ) event_stream.response.close() @parametrize def test_raw_response_stream(self, client: Anthropic) -> None: response = client.beta.sessions.events.with_raw_response.stream( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert response.http_request.headers.get("X-Stainless-Lang") == "python" stream = response.parse() stream.close() @parametrize def test_streaming_response_stream(self, client: Anthropic) -> None: with client.beta.sessions.events.with_streaming_response.stream( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" stream = response.parse() stream.close() assert cast(Any, response.is_closed) is True @parametrize def test_path_params_stream(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): client.beta.sessions.events.with_raw_response.stream( session_id="", ) class TestAsyncEvents: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list(self, async_client: AsyncAnthropic) -> None: event = await async_client.beta.sessions.events.list( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert_matches_type(AsyncPageCursor[BetaManagedAgentsSessionEvent], event, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncAnthropic) -> None: event = await async_client.beta.sessions.events.list( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", created_at_gt=parse_datetime("2019-12-27T18:11:19.117Z"), created_at_gte=parse_datetime("2019-12-27T18:11:19.117Z"), created_at_lt=parse_datetime("2019-12-27T18:11:19.117Z"), created_at_lte=parse_datetime("2019-12-27T18:11:19.117Z"), limit=0, order="asc", page="page", types=["string"], betas=["string"], ) assert_matches_type(AsyncPageCursor[BetaManagedAgentsSessionEvent], event, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_raw_response_list(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.sessions.events.with_raw_response.list( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" event = response.parse() assert_matches_type(AsyncPageCursor[BetaManagedAgentsSessionEvent], event, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_streaming_response_list(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.sessions.events.with_streaming_response.list( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" event = await response.parse() assert_matches_type(AsyncPageCursor[BetaManagedAgentsSessionEvent], event, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_path_params_list(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): await async_client.beta.sessions.events.with_raw_response.list( session_id="", ) @parametrize async def test_method_send(self, async_client: AsyncAnthropic) -> None: event = await async_client.beta.sessions.events.send( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", events=[ { "content": [ { "text": "Where is my order #1234?", "type": "text", } ], "type": "user.message", } ], ) assert_matches_type(BetaManagedAgentsSendSessionEvents, event, path=["response"]) @parametrize async def test_method_send_with_all_params(self, async_client: AsyncAnthropic) -> None: event = await async_client.beta.sessions.events.send( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", events=[ { "content": [ { "text": "Where is my order #1234?", "type": "text", } ], "type": "user.message", } ], betas=["string"], ) assert_matches_type(BetaManagedAgentsSendSessionEvents, event, path=["response"]) @parametrize async def test_raw_response_send(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.sessions.events.with_raw_response.send( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", events=[ { "content": [ { "text": "Where is my order #1234?", "type": "text", } ], "type": "user.message", } ], ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" event = response.parse() assert_matches_type(BetaManagedAgentsSendSessionEvents, event, path=["response"]) @parametrize async def test_streaming_response_send(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.sessions.events.with_streaming_response.send( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", events=[ { "content": [ { "text": "Where is my order #1234?", "type": "text", } ], "type": "user.message", } ], ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" event = await response.parse() assert_matches_type(BetaManagedAgentsSendSessionEvents, event, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_send(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): await async_client.beta.sessions.events.with_raw_response.send( session_id="", events=[ { "content": [ { "text": "Where is my order #1234?", "type": "text", } ], "type": "user.message", } ], ) @parametrize async def test_method_stream(self, async_client: AsyncAnthropic) -> None: event_stream = await async_client.beta.sessions.events.stream( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) await event_stream.response.aclose() @parametrize async def test_method_stream_with_all_params(self, async_client: AsyncAnthropic) -> None: event_stream = await async_client.beta.sessions.events.stream( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", event_deltas=["agent.message"], betas=["string"], ) await event_stream.response.aclose() @parametrize async def test_raw_response_stream(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.sessions.events.with_raw_response.stream( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert response.http_request.headers.get("X-Stainless-Lang") == "python" stream = response.parse() await stream.close() @parametrize async def test_streaming_response_stream(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.sessions.events.with_streaming_response.stream( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" stream = await response.parse() await stream.close() assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_stream(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): await async_client.beta.sessions.events.with_raw_response.stream( session_id="", ) anthropic-sdk-python-0.120.2/tests/api_resources/beta/sessions/test_resources.py000066400000000000000000000734661523216435200301750ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os from typing import Any, cast import pytest from anthropic import Anthropic, AsyncAnthropic from tests.utils import assert_matches_type from anthropic.pagination import SyncPageCursor, AsyncPageCursor from anthropic.types.beta.sessions import ( ResourceUpdateResponse, ResourceRetrieveResponse, BetaManagedAgentsFileResource, BetaManagedAgentsSessionResource, BetaManagedAgentsDeleteSessionResource, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestResources: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize def test_method_retrieve(self, client: Anthropic) -> None: resource = client.beta.sessions.resources.retrieve( resource_id="sesrsc_011CZkZBJq5dWxk9fVLNcPht", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert_matches_type(ResourceRetrieveResponse, resource, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize def test_method_retrieve_with_all_params(self, client: Anthropic) -> None: resource = client.beta.sessions.resources.retrieve( resource_id="sesrsc_011CZkZBJq5dWxk9fVLNcPht", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", betas=["string"], ) assert_matches_type(ResourceRetrieveResponse, resource, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize def test_raw_response_retrieve(self, client: Anthropic) -> None: response = client.beta.sessions.resources.with_raw_response.retrieve( resource_id="sesrsc_011CZkZBJq5dWxk9fVLNcPht", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" resource = response.parse() assert_matches_type(ResourceRetrieveResponse, resource, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize def test_streaming_response_retrieve(self, client: Anthropic) -> None: with client.beta.sessions.resources.with_streaming_response.retrieve( resource_id="sesrsc_011CZkZBJq5dWxk9fVLNcPht", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" resource = response.parse() assert_matches_type(ResourceRetrieveResponse, resource, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize def test_path_params_retrieve(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): client.beta.sessions.resources.with_raw_response.retrieve( resource_id="sesrsc_011CZkZBJq5dWxk9fVLNcPht", session_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `resource_id` but received ''"): client.beta.sessions.resources.with_raw_response.retrieve( resource_id="", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize def test_method_update(self, client: Anthropic) -> None: resource = client.beta.sessions.resources.update( resource_id="sesrsc_011CZkZBJq5dWxk9fVLNcPht", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", authorization_token="ghp_exampletoken", ) assert_matches_type(ResourceUpdateResponse, resource, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize def test_method_update_with_all_params(self, client: Anthropic) -> None: resource = client.beta.sessions.resources.update( resource_id="sesrsc_011CZkZBJq5dWxk9fVLNcPht", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", authorization_token="ghp_exampletoken", betas=["string"], ) assert_matches_type(ResourceUpdateResponse, resource, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize def test_raw_response_update(self, client: Anthropic) -> None: response = client.beta.sessions.resources.with_raw_response.update( resource_id="sesrsc_011CZkZBJq5dWxk9fVLNcPht", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", authorization_token="ghp_exampletoken", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" resource = response.parse() assert_matches_type(ResourceUpdateResponse, resource, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize def test_streaming_response_update(self, client: Anthropic) -> None: with client.beta.sessions.resources.with_streaming_response.update( resource_id="sesrsc_011CZkZBJq5dWxk9fVLNcPht", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", authorization_token="ghp_exampletoken", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" resource = response.parse() assert_matches_type(ResourceUpdateResponse, resource, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize def test_path_params_update(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): client.beta.sessions.resources.with_raw_response.update( resource_id="sesrsc_011CZkZBJq5dWxk9fVLNcPht", session_id="", authorization_token="ghp_exampletoken", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `resource_id` but received ''"): client.beta.sessions.resources.with_raw_response.update( resource_id="", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", authorization_token="ghp_exampletoken", ) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize def test_method_list(self, client: Anthropic) -> None: resource = client.beta.sessions.resources.list( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert_matches_type(SyncPageCursor[BetaManagedAgentsSessionResource], resource, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize def test_method_list_with_all_params(self, client: Anthropic) -> None: resource = client.beta.sessions.resources.list( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", limit=0, page="page", betas=["string"], ) assert_matches_type(SyncPageCursor[BetaManagedAgentsSessionResource], resource, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize def test_raw_response_list(self, client: Anthropic) -> None: response = client.beta.sessions.resources.with_raw_response.list( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" resource = response.parse() assert_matches_type(SyncPageCursor[BetaManagedAgentsSessionResource], resource, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize def test_streaming_response_list(self, client: Anthropic) -> None: with client.beta.sessions.resources.with_streaming_response.list( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" resource = response.parse() assert_matches_type(SyncPageCursor[BetaManagedAgentsSessionResource], resource, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize def test_path_params_list(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): client.beta.sessions.resources.with_raw_response.list( session_id="", ) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize def test_method_delete(self, client: Anthropic) -> None: resource = client.beta.sessions.resources.delete( resource_id="sesrsc_011CZkZBJq5dWxk9fVLNcPht", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert_matches_type(BetaManagedAgentsDeleteSessionResource, resource, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize def test_method_delete_with_all_params(self, client: Anthropic) -> None: resource = client.beta.sessions.resources.delete( resource_id="sesrsc_011CZkZBJq5dWxk9fVLNcPht", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", betas=["string"], ) assert_matches_type(BetaManagedAgentsDeleteSessionResource, resource, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize def test_raw_response_delete(self, client: Anthropic) -> None: response = client.beta.sessions.resources.with_raw_response.delete( resource_id="sesrsc_011CZkZBJq5dWxk9fVLNcPht", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" resource = response.parse() assert_matches_type(BetaManagedAgentsDeleteSessionResource, resource, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize def test_streaming_response_delete(self, client: Anthropic) -> None: with client.beta.sessions.resources.with_streaming_response.delete( resource_id="sesrsc_011CZkZBJq5dWxk9fVLNcPht", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" resource = response.parse() assert_matches_type(BetaManagedAgentsDeleteSessionResource, resource, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize def test_path_params_delete(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): client.beta.sessions.resources.with_raw_response.delete( resource_id="sesrsc_011CZkZBJq5dWxk9fVLNcPht", session_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `resource_id` but received ''"): client.beta.sessions.resources.with_raw_response.delete( resource_id="", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize def test_method_add(self, client: Anthropic) -> None: resource = client.beta.sessions.resources.add( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", file_id="file_011CNha8iCJcU1wXNR6q4V8w", type="file", ) assert_matches_type(BetaManagedAgentsFileResource, resource, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize def test_method_add_with_all_params(self, client: Anthropic) -> None: resource = client.beta.sessions.resources.add( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", file_id="file_011CNha8iCJcU1wXNR6q4V8w", type="file", mount_path="/uploads/receipt.pdf", betas=["string"], ) assert_matches_type(BetaManagedAgentsFileResource, resource, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize def test_raw_response_add(self, client: Anthropic) -> None: response = client.beta.sessions.resources.with_raw_response.add( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", file_id="file_011CNha8iCJcU1wXNR6q4V8w", type="file", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" resource = response.parse() assert_matches_type(BetaManagedAgentsFileResource, resource, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize def test_streaming_response_add(self, client: Anthropic) -> None: with client.beta.sessions.resources.with_streaming_response.add( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", file_id="file_011CNha8iCJcU1wXNR6q4V8w", type="file", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" resource = response.parse() assert_matches_type(BetaManagedAgentsFileResource, resource, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize def test_path_params_add(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): client.beta.sessions.resources.with_raw_response.add( session_id="", file_id="file_011CNha8iCJcU1wXNR6q4V8w", type="file", ) class TestAsyncResources: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize async def test_method_retrieve(self, async_client: AsyncAnthropic) -> None: resource = await async_client.beta.sessions.resources.retrieve( resource_id="sesrsc_011CZkZBJq5dWxk9fVLNcPht", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert_matches_type(ResourceRetrieveResponse, resource, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize async def test_method_retrieve_with_all_params(self, async_client: AsyncAnthropic) -> None: resource = await async_client.beta.sessions.resources.retrieve( resource_id="sesrsc_011CZkZBJq5dWxk9fVLNcPht", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", betas=["string"], ) assert_matches_type(ResourceRetrieveResponse, resource, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.sessions.resources.with_raw_response.retrieve( resource_id="sesrsc_011CZkZBJq5dWxk9fVLNcPht", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" resource = response.parse() assert_matches_type(ResourceRetrieveResponse, resource, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.sessions.resources.with_streaming_response.retrieve( resource_id="sesrsc_011CZkZBJq5dWxk9fVLNcPht", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" resource = await response.parse() assert_matches_type(ResourceRetrieveResponse, resource, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize async def test_path_params_retrieve(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): await async_client.beta.sessions.resources.with_raw_response.retrieve( resource_id="sesrsc_011CZkZBJq5dWxk9fVLNcPht", session_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `resource_id` but received ''"): await async_client.beta.sessions.resources.with_raw_response.retrieve( resource_id="", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize async def test_method_update(self, async_client: AsyncAnthropic) -> None: resource = await async_client.beta.sessions.resources.update( resource_id="sesrsc_011CZkZBJq5dWxk9fVLNcPht", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", authorization_token="ghp_exampletoken", ) assert_matches_type(ResourceUpdateResponse, resource, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize async def test_method_update_with_all_params(self, async_client: AsyncAnthropic) -> None: resource = await async_client.beta.sessions.resources.update( resource_id="sesrsc_011CZkZBJq5dWxk9fVLNcPht", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", authorization_token="ghp_exampletoken", betas=["string"], ) assert_matches_type(ResourceUpdateResponse, resource, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize async def test_raw_response_update(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.sessions.resources.with_raw_response.update( resource_id="sesrsc_011CZkZBJq5dWxk9fVLNcPht", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", authorization_token="ghp_exampletoken", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" resource = response.parse() assert_matches_type(ResourceUpdateResponse, resource, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize async def test_streaming_response_update(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.sessions.resources.with_streaming_response.update( resource_id="sesrsc_011CZkZBJq5dWxk9fVLNcPht", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", authorization_token="ghp_exampletoken", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" resource = await response.parse() assert_matches_type(ResourceUpdateResponse, resource, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize async def test_path_params_update(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): await async_client.beta.sessions.resources.with_raw_response.update( resource_id="sesrsc_011CZkZBJq5dWxk9fVLNcPht", session_id="", authorization_token="ghp_exampletoken", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `resource_id` but received ''"): await async_client.beta.sessions.resources.with_raw_response.update( resource_id="", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", authorization_token="ghp_exampletoken", ) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize async def test_method_list(self, async_client: AsyncAnthropic) -> None: resource = await async_client.beta.sessions.resources.list( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert_matches_type(AsyncPageCursor[BetaManagedAgentsSessionResource], resource, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncAnthropic) -> None: resource = await async_client.beta.sessions.resources.list( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", limit=0, page="page", betas=["string"], ) assert_matches_type(AsyncPageCursor[BetaManagedAgentsSessionResource], resource, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize async def test_raw_response_list(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.sessions.resources.with_raw_response.list( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" resource = response.parse() assert_matches_type(AsyncPageCursor[BetaManagedAgentsSessionResource], resource, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize async def test_streaming_response_list(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.sessions.resources.with_streaming_response.list( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" resource = await response.parse() assert_matches_type(AsyncPageCursor[BetaManagedAgentsSessionResource], resource, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize async def test_path_params_list(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): await async_client.beta.sessions.resources.with_raw_response.list( session_id="", ) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize async def test_method_delete(self, async_client: AsyncAnthropic) -> None: resource = await async_client.beta.sessions.resources.delete( resource_id="sesrsc_011CZkZBJq5dWxk9fVLNcPht", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert_matches_type(BetaManagedAgentsDeleteSessionResource, resource, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize async def test_method_delete_with_all_params(self, async_client: AsyncAnthropic) -> None: resource = await async_client.beta.sessions.resources.delete( resource_id="sesrsc_011CZkZBJq5dWxk9fVLNcPht", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", betas=["string"], ) assert_matches_type(BetaManagedAgentsDeleteSessionResource, resource, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize async def test_raw_response_delete(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.sessions.resources.with_raw_response.delete( resource_id="sesrsc_011CZkZBJq5dWxk9fVLNcPht", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" resource = response.parse() assert_matches_type(BetaManagedAgentsDeleteSessionResource, resource, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize async def test_streaming_response_delete(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.sessions.resources.with_streaming_response.delete( resource_id="sesrsc_011CZkZBJq5dWxk9fVLNcPht", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" resource = await response.parse() assert_matches_type(BetaManagedAgentsDeleteSessionResource, resource, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize async def test_path_params_delete(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): await async_client.beta.sessions.resources.with_raw_response.delete( resource_id="sesrsc_011CZkZBJq5dWxk9fVLNcPht", session_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `resource_id` but received ''"): await async_client.beta.sessions.resources.with_raw_response.delete( resource_id="", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize async def test_method_add(self, async_client: AsyncAnthropic) -> None: resource = await async_client.beta.sessions.resources.add( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", file_id="file_011CNha8iCJcU1wXNR6q4V8w", type="file", ) assert_matches_type(BetaManagedAgentsFileResource, resource, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize async def test_method_add_with_all_params(self, async_client: AsyncAnthropic) -> None: resource = await async_client.beta.sessions.resources.add( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", file_id="file_011CNha8iCJcU1wXNR6q4V8w", type="file", mount_path="/uploads/receipt.pdf", betas=["string"], ) assert_matches_type(BetaManagedAgentsFileResource, resource, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize async def test_raw_response_add(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.sessions.resources.with_raw_response.add( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", file_id="file_011CNha8iCJcU1wXNR6q4V8w", type="file", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" resource = response.parse() assert_matches_type(BetaManagedAgentsFileResource, resource, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize async def test_streaming_response_add(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.sessions.resources.with_streaming_response.add( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", file_id="file_011CNha8iCJcU1wXNR6q4V8w", type="file", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" resource = await response.parse() assert_matches_type(BetaManagedAgentsFileResource, resource, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize async def test_path_params_add(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): await async_client.beta.sessions.resources.with_raw_response.add( session_id="", file_id="file_011CNha8iCJcU1wXNR6q4V8w", type="file", ) anthropic-sdk-python-0.120.2/tests/api_resources/beta/sessions/test_threads.py000066400000000000000000000400221523216435200275730ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os from typing import Any, cast import pytest from anthropic import Anthropic, AsyncAnthropic from tests.utils import assert_matches_type from anthropic.pagination import SyncPageCursor, AsyncPageCursor from anthropic.types.beta.sessions import BetaManagedAgentsSessionThread base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestThreads: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_retrieve(self, client: Anthropic) -> None: thread = client.beta.sessions.threads.retrieve( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert_matches_type(BetaManagedAgentsSessionThread, thread, path=["response"]) @parametrize def test_method_retrieve_with_all_params(self, client: Anthropic) -> None: thread = client.beta.sessions.threads.retrieve( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsSessionThread, thread, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: Anthropic) -> None: response = client.beta.sessions.threads.with_raw_response.retrieve( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" thread = response.parse() assert_matches_type(BetaManagedAgentsSessionThread, thread, path=["response"]) @parametrize def test_streaming_response_retrieve(self, client: Anthropic) -> None: with client.beta.sessions.threads.with_streaming_response.retrieve( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" thread = response.parse() assert_matches_type(BetaManagedAgentsSessionThread, thread, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_retrieve(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): client.beta.sessions.threads.with_raw_response.retrieve( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): client.beta.sessions.threads.with_raw_response.retrieve( thread_id="", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list(self, client: Anthropic) -> None: thread = client.beta.sessions.threads.list( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert_matches_type(SyncPageCursor[BetaManagedAgentsSessionThread], thread, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list_with_all_params(self, client: Anthropic) -> None: thread = client.beta.sessions.threads.list( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", limit=0, page="page", betas=["message-batches-2024-09-24"], ) assert_matches_type(SyncPageCursor[BetaManagedAgentsSessionThread], thread, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_raw_response_list(self, client: Anthropic) -> None: response = client.beta.sessions.threads.with_raw_response.list( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" thread = response.parse() assert_matches_type(SyncPageCursor[BetaManagedAgentsSessionThread], thread, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_streaming_response_list(self, client: Anthropic) -> None: with client.beta.sessions.threads.with_streaming_response.list( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" thread = response.parse() assert_matches_type(SyncPageCursor[BetaManagedAgentsSessionThread], thread, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_path_params_list(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): client.beta.sessions.threads.with_raw_response.list( session_id="", ) @parametrize def test_method_archive(self, client: Anthropic) -> None: thread = client.beta.sessions.threads.archive( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert_matches_type(BetaManagedAgentsSessionThread, thread, path=["response"]) @parametrize def test_method_archive_with_all_params(self, client: Anthropic) -> None: thread = client.beta.sessions.threads.archive( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsSessionThread, thread, path=["response"]) @parametrize def test_raw_response_archive(self, client: Anthropic) -> None: response = client.beta.sessions.threads.with_raw_response.archive( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" thread = response.parse() assert_matches_type(BetaManagedAgentsSessionThread, thread, path=["response"]) @parametrize def test_streaming_response_archive(self, client: Anthropic) -> None: with client.beta.sessions.threads.with_streaming_response.archive( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" thread = response.parse() assert_matches_type(BetaManagedAgentsSessionThread, thread, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_archive(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): client.beta.sessions.threads.with_raw_response.archive( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): client.beta.sessions.threads.with_raw_response.archive( thread_id="", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) class TestAsyncThreads: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize async def test_method_retrieve(self, async_client: AsyncAnthropic) -> None: thread = await async_client.beta.sessions.threads.retrieve( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert_matches_type(BetaManagedAgentsSessionThread, thread, path=["response"]) @parametrize async def test_method_retrieve_with_all_params(self, async_client: AsyncAnthropic) -> None: thread = await async_client.beta.sessions.threads.retrieve( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsSessionThread, thread, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.sessions.threads.with_raw_response.retrieve( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" thread = response.parse() assert_matches_type(BetaManagedAgentsSessionThread, thread, path=["response"]) @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.sessions.threads.with_streaming_response.retrieve( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" thread = await response.parse() assert_matches_type(BetaManagedAgentsSessionThread, thread, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_retrieve(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): await async_client.beta.sessions.threads.with_raw_response.retrieve( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): await async_client.beta.sessions.threads.with_raw_response.retrieve( thread_id="", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list(self, async_client: AsyncAnthropic) -> None: thread = await async_client.beta.sessions.threads.list( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert_matches_type(AsyncPageCursor[BetaManagedAgentsSessionThread], thread, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncAnthropic) -> None: thread = await async_client.beta.sessions.threads.list( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", limit=0, page="page", betas=["message-batches-2024-09-24"], ) assert_matches_type(AsyncPageCursor[BetaManagedAgentsSessionThread], thread, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_raw_response_list(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.sessions.threads.with_raw_response.list( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" thread = response.parse() assert_matches_type(AsyncPageCursor[BetaManagedAgentsSessionThread], thread, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_streaming_response_list(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.sessions.threads.with_streaming_response.list( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" thread = await response.parse() assert_matches_type(AsyncPageCursor[BetaManagedAgentsSessionThread], thread, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_path_params_list(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): await async_client.beta.sessions.threads.with_raw_response.list( session_id="", ) @parametrize async def test_method_archive(self, async_client: AsyncAnthropic) -> None: thread = await async_client.beta.sessions.threads.archive( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert_matches_type(BetaManagedAgentsSessionThread, thread, path=["response"]) @parametrize async def test_method_archive_with_all_params(self, async_client: AsyncAnthropic) -> None: thread = await async_client.beta.sessions.threads.archive( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsSessionThread, thread, path=["response"]) @parametrize async def test_raw_response_archive(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.sessions.threads.with_raw_response.archive( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" thread = response.parse() assert_matches_type(BetaManagedAgentsSessionThread, thread, path=["response"]) @parametrize async def test_streaming_response_archive(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.sessions.threads.with_streaming_response.archive( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" thread = await response.parse() assert_matches_type(BetaManagedAgentsSessionThread, thread, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_archive(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): await async_client.beta.sessions.threads.with_raw_response.archive( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): await async_client.beta.sessions.threads.with_raw_response.archive( thread_id="", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) anthropic-sdk-python-0.120.2/tests/api_resources/beta/sessions/threads/000077500000000000000000000000001523216435200261645ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/api_resources/beta/sessions/threads/__init__.py000066400000000000000000000001261523216435200302740ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. anthropic-sdk-python-0.120.2/tests/api_resources/beta/sessions/threads/test_events.py000066400000000000000000000273221523216435200311070ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os from typing import Any, cast import pytest from anthropic import Anthropic, AsyncAnthropic from tests.utils import assert_matches_type from anthropic.pagination import SyncPageCursor, AsyncPageCursor from anthropic.types.beta.sessions import BetaManagedAgentsSessionEvent base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestEvents: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list(self, client: Anthropic) -> None: event = client.beta.sessions.threads.events.list( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert_matches_type(SyncPageCursor[BetaManagedAgentsSessionEvent], event, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list_with_all_params(self, client: Anthropic) -> None: event = client.beta.sessions.threads.events.list( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", limit=0, page="page", betas=["message-batches-2024-09-24"], ) assert_matches_type(SyncPageCursor[BetaManagedAgentsSessionEvent], event, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_raw_response_list(self, client: Anthropic) -> None: response = client.beta.sessions.threads.events.with_raw_response.list( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" event = response.parse() assert_matches_type(SyncPageCursor[BetaManagedAgentsSessionEvent], event, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_streaming_response_list(self, client: Anthropic) -> None: with client.beta.sessions.threads.events.with_streaming_response.list( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" event = response.parse() assert_matches_type(SyncPageCursor[BetaManagedAgentsSessionEvent], event, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_path_params_list(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): client.beta.sessions.threads.events.with_raw_response.list( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): client.beta.sessions.threads.events.with_raw_response.list( thread_id="", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) @parametrize def test_method_stream(self, client: Anthropic) -> None: event_stream = client.beta.sessions.threads.events.stream( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) event_stream.response.close() @parametrize def test_method_stream_with_all_params(self, client: Anthropic) -> None: event_stream = client.beta.sessions.threads.events.stream( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", event_deltas=["agent.message"], betas=["message-batches-2024-09-24"], ) event_stream.response.close() @parametrize def test_raw_response_stream(self, client: Anthropic) -> None: response = client.beta.sessions.threads.events.with_raw_response.stream( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert response.http_request.headers.get("X-Stainless-Lang") == "python" stream = response.parse() stream.close() @parametrize def test_streaming_response_stream(self, client: Anthropic) -> None: with client.beta.sessions.threads.events.with_streaming_response.stream( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" stream = response.parse() stream.close() assert cast(Any, response.is_closed) is True @parametrize def test_path_params_stream(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): client.beta.sessions.threads.events.with_raw_response.stream( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): client.beta.sessions.threads.events.with_raw_response.stream( thread_id="", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) class TestAsyncEvents: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list(self, async_client: AsyncAnthropic) -> None: event = await async_client.beta.sessions.threads.events.list( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert_matches_type(AsyncPageCursor[BetaManagedAgentsSessionEvent], event, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncAnthropic) -> None: event = await async_client.beta.sessions.threads.events.list( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", limit=0, page="page", betas=["message-batches-2024-09-24"], ) assert_matches_type(AsyncPageCursor[BetaManagedAgentsSessionEvent], event, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_raw_response_list(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.sessions.threads.events.with_raw_response.list( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" event = response.parse() assert_matches_type(AsyncPageCursor[BetaManagedAgentsSessionEvent], event, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_streaming_response_list(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.sessions.threads.events.with_streaming_response.list( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" event = await response.parse() assert_matches_type(AsyncPageCursor[BetaManagedAgentsSessionEvent], event, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_path_params_list(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): await async_client.beta.sessions.threads.events.with_raw_response.list( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): await async_client.beta.sessions.threads.events.with_raw_response.list( thread_id="", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) @parametrize async def test_method_stream(self, async_client: AsyncAnthropic) -> None: event_stream = await async_client.beta.sessions.threads.events.stream( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) await event_stream.response.aclose() @parametrize async def test_method_stream_with_all_params(self, async_client: AsyncAnthropic) -> None: event_stream = await async_client.beta.sessions.threads.events.stream( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", event_deltas=["agent.message"], betas=["message-batches-2024-09-24"], ) await event_stream.response.aclose() @parametrize async def test_raw_response_stream(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.sessions.threads.events.with_raw_response.stream( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert response.http_request.headers.get("X-Stainless-Lang") == "python" stream = response.parse() await stream.close() @parametrize async def test_streaming_response_stream(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.sessions.threads.events.with_streaming_response.stream( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" stream = await response.parse() await stream.close() assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_stream(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): await async_client.beta.sessions.threads.events.with_raw_response.stream( thread_id="sthr_011CZkZVWa6oIjw0rgXZpnBt", session_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `thread_id` but received ''"): await async_client.beta.sessions.threads.events.with_raw_response.stream( thread_id="", session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) anthropic-sdk-python-0.120.2/tests/api_resources/beta/skills/000077500000000000000000000000001523216435200241655ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/api_resources/beta/skills/__init__.py000066400000000000000000000001261523216435200262750ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. anthropic-sdk-python-0.120.2/tests/api_resources/beta/skills/test_versions.py000066400000000000000000000621701523216435200274540ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os from typing import Any, cast import httpx import pytest from respx import MockRouter from anthropic import Anthropic, AsyncAnthropic from tests.utils import assert_matches_type from anthropic._response import ( BinaryAPIResponse, AsyncBinaryAPIResponse, StreamedBinaryAPIResponse, AsyncStreamedBinaryAPIResponse, ) from anthropic.pagination import SyncPageCursor, AsyncPageCursor from anthropic.types.beta.skills import ( VersionListResponse, VersionCreateResponse, VersionDeleteResponse, VersionRetrieveResponse, ) # pyright: reportDeprecated=false base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestVersions: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create(self, client: Anthropic) -> None: version = client.beta.skills.versions.create( skill_id="skill_id", files=[b"Example data"], ) assert_matches_type(VersionCreateResponse, version, path=["response"]) @parametrize def test_method_create_with_all_params(self, client: Anthropic) -> None: version = client.beta.skills.versions.create( skill_id="skill_id", files=[b"Example data"], betas=["string"], ) assert_matches_type(VersionCreateResponse, version, path=["response"]) @parametrize def test_raw_response_create(self, client: Anthropic) -> None: response = client.beta.skills.versions.with_raw_response.create( skill_id="skill_id", files=[b"Example data"], ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" version = response.parse() assert_matches_type(VersionCreateResponse, version, path=["response"]) @parametrize def test_streaming_response_create(self, client: Anthropic) -> None: with client.beta.skills.versions.with_streaming_response.create( skill_id="skill_id", files=[b"Example data"], ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" version = response.parse() assert_matches_type(VersionCreateResponse, version, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_create(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): client.beta.skills.versions.with_raw_response.create( skill_id="", files=[b"Example data"], ) @parametrize def test_method_retrieve(self, client: Anthropic) -> None: version = client.beta.skills.versions.retrieve( version="version", skill_id="skill_id", ) assert_matches_type(VersionRetrieveResponse, version, path=["response"]) @parametrize def test_method_retrieve_with_all_params(self, client: Anthropic) -> None: version = client.beta.skills.versions.retrieve( version="version", skill_id="skill_id", betas=["string"], ) assert_matches_type(VersionRetrieveResponse, version, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: Anthropic) -> None: response = client.beta.skills.versions.with_raw_response.retrieve( version="version", skill_id="skill_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" version = response.parse() assert_matches_type(VersionRetrieveResponse, version, path=["response"]) @parametrize def test_streaming_response_retrieve(self, client: Anthropic) -> None: with client.beta.skills.versions.with_streaming_response.retrieve( version="version", skill_id="skill_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" version = response.parse() assert_matches_type(VersionRetrieveResponse, version, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_retrieve(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): client.beta.skills.versions.with_raw_response.retrieve( version="version", skill_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `version` but received ''"): client.beta.skills.versions.with_raw_response.retrieve( version="", skill_id="skill_id", ) @parametrize def test_method_list(self, client: Anthropic) -> None: version = client.beta.skills.versions.list( skill_id="skill_id", ) assert_matches_type(SyncPageCursor[VersionListResponse], version, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: Anthropic) -> None: version = client.beta.skills.versions.list( skill_id="skill_id", limit=0, page="page", betas=["string"], ) assert_matches_type(SyncPageCursor[VersionListResponse], version, path=["response"]) @parametrize def test_raw_response_list(self, client: Anthropic) -> None: response = client.beta.skills.versions.with_raw_response.list( skill_id="skill_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" version = response.parse() assert_matches_type(SyncPageCursor[VersionListResponse], version, path=["response"]) @parametrize def test_streaming_response_list(self, client: Anthropic) -> None: with client.beta.skills.versions.with_streaming_response.list( skill_id="skill_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" version = response.parse() assert_matches_type(SyncPageCursor[VersionListResponse], version, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_list(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): client.beta.skills.versions.with_raw_response.list( skill_id="", ) @parametrize def test_method_delete(self, client: Anthropic) -> None: version = client.beta.skills.versions.delete( version="version", skill_id="skill_id", ) assert_matches_type(VersionDeleteResponse, version, path=["response"]) @parametrize def test_method_delete_with_all_params(self, client: Anthropic) -> None: version = client.beta.skills.versions.delete( version="version", skill_id="skill_id", betas=["string"], ) assert_matches_type(VersionDeleteResponse, version, path=["response"]) @parametrize def test_raw_response_delete(self, client: Anthropic) -> None: response = client.beta.skills.versions.with_raw_response.delete( version="version", skill_id="skill_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" version = response.parse() assert_matches_type(VersionDeleteResponse, version, path=["response"]) @parametrize def test_streaming_response_delete(self, client: Anthropic) -> None: with client.beta.skills.versions.with_streaming_response.delete( version="version", skill_id="skill_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" version = response.parse() assert_matches_type(VersionDeleteResponse, version, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_delete(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): client.beta.skills.versions.with_raw_response.delete( version="version", skill_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `version` but received ''"): client.beta.skills.versions.with_raw_response.delete( version="", skill_id="skill_id", ) @parametrize @pytest.mark.respx(base_url=base_url) def test_method_download(self, client: Anthropic, respx_mock: MockRouter) -> None: respx_mock.get("/v1/skills/skill_id/versions/version/content?beta=true").mock( return_value=httpx.Response(200, json={"foo": "bar"}) ) version = client.beta.skills.versions.download( version="version", skill_id="skill_id", ) assert version.is_closed assert version.json() == {"foo": "bar"} assert cast(Any, version.is_closed) is True assert isinstance(version, BinaryAPIResponse) @parametrize @pytest.mark.respx(base_url=base_url) def test_method_download_with_all_params(self, client: Anthropic, respx_mock: MockRouter) -> None: respx_mock.get("/v1/skills/skill_id/versions/version/content?beta=true").mock( return_value=httpx.Response(200, json={"foo": "bar"}) ) version = client.beta.skills.versions.download( version="version", skill_id="skill_id", betas=["message-batches-2024-09-24"], ) assert version.is_closed assert version.json() == {"foo": "bar"} assert cast(Any, version.is_closed) is True assert isinstance(version, BinaryAPIResponse) @parametrize @pytest.mark.respx(base_url=base_url) def test_raw_response_download(self, client: Anthropic, respx_mock: MockRouter) -> None: respx_mock.get("/v1/skills/skill_id/versions/version/content?beta=true").mock( return_value=httpx.Response(200, json={"foo": "bar"}) ) version = client.beta.skills.versions.with_raw_response.download( version="version", skill_id="skill_id", ) assert version.is_closed is True assert version.http_request.headers.get("X-Stainless-Lang") == "python" assert version.json() == {"foo": "bar"} assert isinstance(version, BinaryAPIResponse) @parametrize @pytest.mark.respx(base_url=base_url) def test_streaming_response_download(self, client: Anthropic, respx_mock: MockRouter) -> None: respx_mock.get("/v1/skills/skill_id/versions/version/content?beta=true").mock( return_value=httpx.Response(200, json={"foo": "bar"}) ) with client.beta.skills.versions.with_streaming_response.download( version="version", skill_id="skill_id", ) as version: assert not version.is_closed assert version.http_request.headers.get("X-Stainless-Lang") == "python" assert version.json() == {"foo": "bar"} assert cast(Any, version.is_closed) is True assert isinstance(version, StreamedBinaryAPIResponse) assert cast(Any, version.is_closed) is True @parametrize @pytest.mark.respx(base_url=base_url) def test_path_params_download(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): client.beta.skills.versions.with_raw_response.download( version="version", skill_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `version` but received ''"): client.beta.skills.versions.with_raw_response.download( version="", skill_id="skill_id", ) class TestAsyncVersions: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize async def test_method_create(self, async_client: AsyncAnthropic) -> None: version = await async_client.beta.skills.versions.create( skill_id="skill_id", files=[b"Example data"], ) assert_matches_type(VersionCreateResponse, version, path=["response"]) @parametrize async def test_method_create_with_all_params(self, async_client: AsyncAnthropic) -> None: version = await async_client.beta.skills.versions.create( skill_id="skill_id", files=[b"Example data"], betas=["string"], ) assert_matches_type(VersionCreateResponse, version, path=["response"]) @parametrize async def test_raw_response_create(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.skills.versions.with_raw_response.create( skill_id="skill_id", files=[b"Example data"], ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" version = response.parse() assert_matches_type(VersionCreateResponse, version, path=["response"]) @parametrize async def test_streaming_response_create(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.skills.versions.with_streaming_response.create( skill_id="skill_id", files=[b"Example data"], ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" version = await response.parse() assert_matches_type(VersionCreateResponse, version, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_create(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): await async_client.beta.skills.versions.with_raw_response.create( skill_id="", files=[b"Example data"], ) @parametrize async def test_method_retrieve(self, async_client: AsyncAnthropic) -> None: version = await async_client.beta.skills.versions.retrieve( version="version", skill_id="skill_id", ) assert_matches_type(VersionRetrieveResponse, version, path=["response"]) @parametrize async def test_method_retrieve_with_all_params(self, async_client: AsyncAnthropic) -> None: version = await async_client.beta.skills.versions.retrieve( version="version", skill_id="skill_id", betas=["string"], ) assert_matches_type(VersionRetrieveResponse, version, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.skills.versions.with_raw_response.retrieve( version="version", skill_id="skill_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" version = response.parse() assert_matches_type(VersionRetrieveResponse, version, path=["response"]) @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.skills.versions.with_streaming_response.retrieve( version="version", skill_id="skill_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" version = await response.parse() assert_matches_type(VersionRetrieveResponse, version, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_retrieve(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): await async_client.beta.skills.versions.with_raw_response.retrieve( version="version", skill_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `version` but received ''"): await async_client.beta.skills.versions.with_raw_response.retrieve( version="", skill_id="skill_id", ) @parametrize async def test_method_list(self, async_client: AsyncAnthropic) -> None: version = await async_client.beta.skills.versions.list( skill_id="skill_id", ) assert_matches_type(AsyncPageCursor[VersionListResponse], version, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncAnthropic) -> None: version = await async_client.beta.skills.versions.list( skill_id="skill_id", limit=0, page="page", betas=["string"], ) assert_matches_type(AsyncPageCursor[VersionListResponse], version, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.skills.versions.with_raw_response.list( skill_id="skill_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" version = response.parse() assert_matches_type(AsyncPageCursor[VersionListResponse], version, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.skills.versions.with_streaming_response.list( skill_id="skill_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" version = await response.parse() assert_matches_type(AsyncPageCursor[VersionListResponse], version, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_list(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): await async_client.beta.skills.versions.with_raw_response.list( skill_id="", ) @parametrize async def test_method_delete(self, async_client: AsyncAnthropic) -> None: version = await async_client.beta.skills.versions.delete( version="version", skill_id="skill_id", ) assert_matches_type(VersionDeleteResponse, version, path=["response"]) @parametrize async def test_method_delete_with_all_params(self, async_client: AsyncAnthropic) -> None: version = await async_client.beta.skills.versions.delete( version="version", skill_id="skill_id", betas=["string"], ) assert_matches_type(VersionDeleteResponse, version, path=["response"]) @parametrize async def test_raw_response_delete(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.skills.versions.with_raw_response.delete( version="version", skill_id="skill_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" version = response.parse() assert_matches_type(VersionDeleteResponse, version, path=["response"]) @parametrize async def test_streaming_response_delete(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.skills.versions.with_streaming_response.delete( version="version", skill_id="skill_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" version = await response.parse() assert_matches_type(VersionDeleteResponse, version, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_delete(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): await async_client.beta.skills.versions.with_raw_response.delete( version="version", skill_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `version` but received ''"): await async_client.beta.skills.versions.with_raw_response.delete( version="", skill_id="skill_id", ) @parametrize @pytest.mark.respx(base_url=base_url) async def test_method_download(self, async_client: AsyncAnthropic, respx_mock: MockRouter) -> None: respx_mock.get("/v1/skills/skill_id/versions/version/content?beta=true").mock( return_value=httpx.Response(200, json={"foo": "bar"}) ) version = await async_client.beta.skills.versions.download( version="version", skill_id="skill_id", ) assert version.is_closed assert await version.json() == {"foo": "bar"} assert cast(Any, version.is_closed) is True assert isinstance(version, AsyncBinaryAPIResponse) @parametrize @pytest.mark.respx(base_url=base_url) async def test_method_download_with_all_params(self, async_client: AsyncAnthropic, respx_mock: MockRouter) -> None: respx_mock.get("/v1/skills/skill_id/versions/version/content?beta=true").mock( return_value=httpx.Response(200, json={"foo": "bar"}) ) version = await async_client.beta.skills.versions.download( version="version", skill_id="skill_id", betas=["message-batches-2024-09-24"], ) assert version.is_closed assert await version.json() == {"foo": "bar"} assert cast(Any, version.is_closed) is True assert isinstance(version, AsyncBinaryAPIResponse) @parametrize @pytest.mark.respx(base_url=base_url) async def test_raw_response_download(self, async_client: AsyncAnthropic, respx_mock: MockRouter) -> None: respx_mock.get("/v1/skills/skill_id/versions/version/content?beta=true").mock( return_value=httpx.Response(200, json={"foo": "bar"}) ) version = await async_client.beta.skills.versions.with_raw_response.download( version="version", skill_id="skill_id", ) assert version.is_closed is True assert version.http_request.headers.get("X-Stainless-Lang") == "python" assert await version.json() == {"foo": "bar"} assert isinstance(version, AsyncBinaryAPIResponse) @parametrize @pytest.mark.respx(base_url=base_url) async def test_streaming_response_download(self, async_client: AsyncAnthropic, respx_mock: MockRouter) -> None: respx_mock.get("/v1/skills/skill_id/versions/version/content?beta=true").mock( return_value=httpx.Response(200, json={"foo": "bar"}) ) async with async_client.beta.skills.versions.with_streaming_response.download( version="version", skill_id="skill_id", ) as version: assert not version.is_closed assert version.http_request.headers.get("X-Stainless-Lang") == "python" assert await version.json() == {"foo": "bar"} assert cast(Any, version.is_closed) is True assert isinstance(version, AsyncStreamedBinaryAPIResponse) assert cast(Any, version.is_closed) is True @parametrize @pytest.mark.respx(base_url=base_url) async def test_path_params_download(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): await async_client.beta.skills.versions.with_raw_response.download( version="version", skill_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `version` but received ''"): await async_client.beta.skills.versions.with_raw_response.download( version="", skill_id="skill_id", ) anthropic-sdk-python-0.120.2/tests/api_resources/beta/test_agents.py000066400000000000000000000643641523216435200255730ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os from typing import Any, cast import pytest from anthropic import Anthropic, AsyncAnthropic from tests.utils import assert_matches_type from anthropic._utils import parse_datetime from anthropic.pagination import SyncPageCursor, AsyncPageCursor from anthropic.types.beta import ( BetaManagedAgentsAgent, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestAgents: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create(self, client: Anthropic) -> None: agent = client.beta.agents.create( model="claude-sonnet-4-6", name="My First Agent", ) assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) @parametrize def test_method_create_with_all_params(self, client: Anthropic) -> None: agent = client.beta.agents.create( model="claude-sonnet-4-6", name="My First Agent", description="A general-purpose starter agent.", mcp_servers=[ { "name": "example-mcp", "type": "url", "url": "https://example-server.modelcontextprotocol.io/sse", } ], metadata={"foo": "bar"}, multiagent={ "agents": ["agent_011CZkYqphY8vELVzwCUpqiQ", {"type": "self"}], "type": "coordinator", }, skills=[ { "skill_id": "xlsx", "type": "anthropic", "version": "1", } ], system="You are a general-purpose agent that can research, write code, run commands, and use connected tools to complete the user's task end to end.", tools=[ { "type": "agent_toolset_20260401", "configs": [ { "name": "bash", "enabled": True, "permission_policy": {"type": "always_allow"}, } ], "default_config": { "enabled": True, "permission_policy": {"type": "always_allow"}, }, } ], betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) @parametrize def test_raw_response_create(self, client: Anthropic) -> None: response = client.beta.agents.with_raw_response.create( model="claude-sonnet-4-6", name="My First Agent", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" agent = response.parse() assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) @parametrize def test_streaming_response_create(self, client: Anthropic) -> None: with client.beta.agents.with_streaming_response.create( model="claude-sonnet-4-6", name="My First Agent", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" agent = response.parse() assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_retrieve(self, client: Anthropic) -> None: agent = client.beta.agents.retrieve( agent_id="agent_011CZkYpogX7uDKUyvBTophP", ) assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_retrieve_with_all_params(self, client: Anthropic) -> None: agent = client.beta.agents.retrieve( agent_id="agent_011CZkYpogX7uDKUyvBTophP", version=0, betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_raw_response_retrieve(self, client: Anthropic) -> None: response = client.beta.agents.with_raw_response.retrieve( agent_id="agent_011CZkYpogX7uDKUyvBTophP", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" agent = response.parse() assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_streaming_response_retrieve(self, client: Anthropic) -> None: with client.beta.agents.with_streaming_response.retrieve( agent_id="agent_011CZkYpogX7uDKUyvBTophP", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" agent = response.parse() assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_path_params_retrieve(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_id` but received ''"): client.beta.agents.with_raw_response.retrieve( agent_id="", ) @parametrize def test_method_update(self, client: Anthropic) -> None: agent = client.beta.agents.update( agent_id="agent_011CZkYpogX7uDKUyvBTophP", ) assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) @parametrize def test_method_update_with_all_params(self, client: Anthropic) -> None: agent = client.beta.agents.update( agent_id="agent_011CZkYpogX7uDKUyvBTophP", description="updated", mcp_servers=[ { "name": "example-mcp", "type": "url", "url": "https://example-server.modelcontextprotocol.io/sse", } ], metadata={"foo": "string"}, model={ "id": "claude-opus-4-8", "effort": "low", "speed": "standard", }, multiagent={ "agents": ["agent_011CZkYqphY8vELVzwCUpqiQ", {"type": "self"}], "type": "coordinator", }, name="name", skills=[ { "skill_id": "xlsx", "type": "anthropic", "version": "1", } ], system="You are a general-purpose agent that can research, write code, run commands, and use connected tools to complete the user's task end to end.", tools=[ { "type": "agent_toolset_20260401", "configs": [ { "name": "bash", "enabled": True, "permission_policy": {"type": "always_allow"}, } ], "default_config": { "enabled": True, "permission_policy": {"type": "always_allow"}, }, } ], version=1, betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) @parametrize def test_raw_response_update(self, client: Anthropic) -> None: response = client.beta.agents.with_raw_response.update( agent_id="agent_011CZkYpogX7uDKUyvBTophP", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" agent = response.parse() assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) @parametrize def test_streaming_response_update(self, client: Anthropic) -> None: with client.beta.agents.with_streaming_response.update( agent_id="agent_011CZkYpogX7uDKUyvBTophP", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" agent = response.parse() assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_update(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_id` but received ''"): client.beta.agents.with_raw_response.update( agent_id="", ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list(self, client: Anthropic) -> None: agent = client.beta.agents.list() assert_matches_type(SyncPageCursor[BetaManagedAgentsAgent], agent, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list_with_all_params(self, client: Anthropic) -> None: agent = client.beta.agents.list( created_at_gte=parse_datetime("2019-12-27T18:11:19.117Z"), created_at_lte=parse_datetime("2019-12-27T18:11:19.117Z"), include_archived=True, limit=0, page="page", betas=["message-batches-2024-09-24"], ) assert_matches_type(SyncPageCursor[BetaManagedAgentsAgent], agent, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_raw_response_list(self, client: Anthropic) -> None: response = client.beta.agents.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" agent = response.parse() assert_matches_type(SyncPageCursor[BetaManagedAgentsAgent], agent, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_streaming_response_list(self, client: Anthropic) -> None: with client.beta.agents.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" agent = response.parse() assert_matches_type(SyncPageCursor[BetaManagedAgentsAgent], agent, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_archive(self, client: Anthropic) -> None: agent = client.beta.agents.archive( agent_id="agent_011CZkYpogX7uDKUyvBTophP", ) assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) @parametrize def test_method_archive_with_all_params(self, client: Anthropic) -> None: agent = client.beta.agents.archive( agent_id="agent_011CZkYpogX7uDKUyvBTophP", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) @parametrize def test_raw_response_archive(self, client: Anthropic) -> None: response = client.beta.agents.with_raw_response.archive( agent_id="agent_011CZkYpogX7uDKUyvBTophP", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" agent = response.parse() assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) @parametrize def test_streaming_response_archive(self, client: Anthropic) -> None: with client.beta.agents.with_streaming_response.archive( agent_id="agent_011CZkYpogX7uDKUyvBTophP", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" agent = response.parse() assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_archive(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_id` but received ''"): client.beta.agents.with_raw_response.archive( agent_id="", ) class TestAsyncAgents: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize async def test_method_create(self, async_client: AsyncAnthropic) -> None: agent = await async_client.beta.agents.create( model="claude-sonnet-4-6", name="My First Agent", ) assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) @parametrize async def test_method_create_with_all_params(self, async_client: AsyncAnthropic) -> None: agent = await async_client.beta.agents.create( model="claude-sonnet-4-6", name="My First Agent", description="A general-purpose starter agent.", mcp_servers=[ { "name": "example-mcp", "type": "url", "url": "https://example-server.modelcontextprotocol.io/sse", } ], metadata={"foo": "bar"}, multiagent={ "agents": ["agent_011CZkYqphY8vELVzwCUpqiQ", {"type": "self"}], "type": "coordinator", }, skills=[ { "skill_id": "xlsx", "type": "anthropic", "version": "1", } ], system="You are a general-purpose agent that can research, write code, run commands, and use connected tools to complete the user's task end to end.", tools=[ { "type": "agent_toolset_20260401", "configs": [ { "name": "bash", "enabled": True, "permission_policy": {"type": "always_allow"}, } ], "default_config": { "enabled": True, "permission_policy": {"type": "always_allow"}, }, } ], betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) @parametrize async def test_raw_response_create(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.agents.with_raw_response.create( model="claude-sonnet-4-6", name="My First Agent", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" agent = response.parse() assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) @parametrize async def test_streaming_response_create(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.agents.with_streaming_response.create( model="claude-sonnet-4-6", name="My First Agent", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" agent = await response.parse() assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_retrieve(self, async_client: AsyncAnthropic) -> None: agent = await async_client.beta.agents.retrieve( agent_id="agent_011CZkYpogX7uDKUyvBTophP", ) assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_retrieve_with_all_params(self, async_client: AsyncAnthropic) -> None: agent = await async_client.beta.agents.retrieve( agent_id="agent_011CZkYpogX7uDKUyvBTophP", version=0, betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.agents.with_raw_response.retrieve( agent_id="agent_011CZkYpogX7uDKUyvBTophP", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" agent = response.parse() assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.agents.with_streaming_response.retrieve( agent_id="agent_011CZkYpogX7uDKUyvBTophP", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" agent = await response.parse() assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_path_params_retrieve(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_id` but received ''"): await async_client.beta.agents.with_raw_response.retrieve( agent_id="", ) @parametrize async def test_method_update(self, async_client: AsyncAnthropic) -> None: agent = await async_client.beta.agents.update( agent_id="agent_011CZkYpogX7uDKUyvBTophP", ) assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) @parametrize async def test_method_update_with_all_params(self, async_client: AsyncAnthropic) -> None: agent = await async_client.beta.agents.update( agent_id="agent_011CZkYpogX7uDKUyvBTophP", description="updated", mcp_servers=[ { "name": "example-mcp", "type": "url", "url": "https://example-server.modelcontextprotocol.io/sse", } ], metadata={"foo": "string"}, model={ "id": "claude-opus-4-8", "effort": "low", "speed": "standard", }, multiagent={ "agents": ["agent_011CZkYqphY8vELVzwCUpqiQ", {"type": "self"}], "type": "coordinator", }, name="name", skills=[ { "skill_id": "xlsx", "type": "anthropic", "version": "1", } ], system="You are a general-purpose agent that can research, write code, run commands, and use connected tools to complete the user's task end to end.", tools=[ { "type": "agent_toolset_20260401", "configs": [ { "name": "bash", "enabled": True, "permission_policy": {"type": "always_allow"}, } ], "default_config": { "enabled": True, "permission_policy": {"type": "always_allow"}, }, } ], version=1, betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) @parametrize async def test_raw_response_update(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.agents.with_raw_response.update( agent_id="agent_011CZkYpogX7uDKUyvBTophP", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" agent = response.parse() assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) @parametrize async def test_streaming_response_update(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.agents.with_streaming_response.update( agent_id="agent_011CZkYpogX7uDKUyvBTophP", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" agent = await response.parse() assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_update(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_id` but received ''"): await async_client.beta.agents.with_raw_response.update( agent_id="", ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list(self, async_client: AsyncAnthropic) -> None: agent = await async_client.beta.agents.list() assert_matches_type(AsyncPageCursor[BetaManagedAgentsAgent], agent, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncAnthropic) -> None: agent = await async_client.beta.agents.list( created_at_gte=parse_datetime("2019-12-27T18:11:19.117Z"), created_at_lte=parse_datetime("2019-12-27T18:11:19.117Z"), include_archived=True, limit=0, page="page", betas=["message-batches-2024-09-24"], ) assert_matches_type(AsyncPageCursor[BetaManagedAgentsAgent], agent, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_raw_response_list(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.agents.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" agent = response.parse() assert_matches_type(AsyncPageCursor[BetaManagedAgentsAgent], agent, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_streaming_response_list(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.agents.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" agent = await response.parse() assert_matches_type(AsyncPageCursor[BetaManagedAgentsAgent], agent, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_archive(self, async_client: AsyncAnthropic) -> None: agent = await async_client.beta.agents.archive( agent_id="agent_011CZkYpogX7uDKUyvBTophP", ) assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) @parametrize async def test_method_archive_with_all_params(self, async_client: AsyncAnthropic) -> None: agent = await async_client.beta.agents.archive( agent_id="agent_011CZkYpogX7uDKUyvBTophP", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) @parametrize async def test_raw_response_archive(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.agents.with_raw_response.archive( agent_id="agent_011CZkYpogX7uDKUyvBTophP", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" agent = response.parse() assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) @parametrize async def test_streaming_response_archive(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.agents.with_streaming_response.archive( agent_id="agent_011CZkYpogX7uDKUyvBTophP", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" agent = await response.parse() assert_matches_type(BetaManagedAgentsAgent, agent, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_archive(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `agent_id` but received ''"): await async_client.beta.agents.with_raw_response.archive( agent_id="", ) anthropic-sdk-python-0.120.2/tests/api_resources/beta/test_deployment_runs.py000066400000000000000000000245601523216435200275330ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os from typing import Any, cast import pytest from anthropic import Anthropic, AsyncAnthropic from tests.utils import assert_matches_type from anthropic._utils import parse_datetime from anthropic.pagination import SyncPageCursor, AsyncPageCursor from anthropic.types.beta import ( BetaManagedAgentsDeploymentRun, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestDeploymentRuns: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_retrieve(self, client: Anthropic) -> None: deployment_run = client.beta.deployment_runs.retrieve( deployment_run_id="deployment_run_id", ) assert_matches_type(BetaManagedAgentsDeploymentRun, deployment_run, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_retrieve_with_all_params(self, client: Anthropic) -> None: deployment_run = client.beta.deployment_runs.retrieve( deployment_run_id="deployment_run_id", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsDeploymentRun, deployment_run, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_raw_response_retrieve(self, client: Anthropic) -> None: response = client.beta.deployment_runs.with_raw_response.retrieve( deployment_run_id="deployment_run_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment_run = response.parse() assert_matches_type(BetaManagedAgentsDeploymentRun, deployment_run, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_streaming_response_retrieve(self, client: Anthropic) -> None: with client.beta.deployment_runs.with_streaming_response.retrieve( deployment_run_id="deployment_run_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment_run = response.parse() assert_matches_type(BetaManagedAgentsDeploymentRun, deployment_run, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_path_params_retrieve(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `deployment_run_id` but received ''"): client.beta.deployment_runs.with_raw_response.retrieve( deployment_run_id="", ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list(self, client: Anthropic) -> None: deployment_run = client.beta.deployment_runs.list() assert_matches_type(SyncPageCursor[BetaManagedAgentsDeploymentRun], deployment_run, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list_with_all_params(self, client: Anthropic) -> None: deployment_run = client.beta.deployment_runs.list( created_at_gt=parse_datetime("2019-12-27T18:11:19.117Z"), created_at_gte=parse_datetime("2019-12-27T18:11:19.117Z"), created_at_lt=parse_datetime("2019-12-27T18:11:19.117Z"), created_at_lte=parse_datetime("2019-12-27T18:11:19.117Z"), deployment_id="deployment_id", has_error=True, limit=0, page="page", trigger_type="schedule", betas=["message-batches-2024-09-24"], ) assert_matches_type(SyncPageCursor[BetaManagedAgentsDeploymentRun], deployment_run, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_raw_response_list(self, client: Anthropic) -> None: response = client.beta.deployment_runs.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment_run = response.parse() assert_matches_type(SyncPageCursor[BetaManagedAgentsDeploymentRun], deployment_run, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_streaming_response_list(self, client: Anthropic) -> None: with client.beta.deployment_runs.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment_run = response.parse() assert_matches_type(SyncPageCursor[BetaManagedAgentsDeploymentRun], deployment_run, path=["response"]) assert cast(Any, response.is_closed) is True class TestAsyncDeploymentRuns: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_retrieve(self, async_client: AsyncAnthropic) -> None: deployment_run = await async_client.beta.deployment_runs.retrieve( deployment_run_id="deployment_run_id", ) assert_matches_type(BetaManagedAgentsDeploymentRun, deployment_run, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_retrieve_with_all_params(self, async_client: AsyncAnthropic) -> None: deployment_run = await async_client.beta.deployment_runs.retrieve( deployment_run_id="deployment_run_id", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsDeploymentRun, deployment_run, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.deployment_runs.with_raw_response.retrieve( deployment_run_id="deployment_run_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment_run = response.parse() assert_matches_type(BetaManagedAgentsDeploymentRun, deployment_run, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.deployment_runs.with_streaming_response.retrieve( deployment_run_id="deployment_run_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment_run = await response.parse() assert_matches_type(BetaManagedAgentsDeploymentRun, deployment_run, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_path_params_retrieve(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `deployment_run_id` but received ''"): await async_client.beta.deployment_runs.with_raw_response.retrieve( deployment_run_id="", ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list(self, async_client: AsyncAnthropic) -> None: deployment_run = await async_client.beta.deployment_runs.list() assert_matches_type(AsyncPageCursor[BetaManagedAgentsDeploymentRun], deployment_run, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncAnthropic) -> None: deployment_run = await async_client.beta.deployment_runs.list( created_at_gt=parse_datetime("2019-12-27T18:11:19.117Z"), created_at_gte=parse_datetime("2019-12-27T18:11:19.117Z"), created_at_lt=parse_datetime("2019-12-27T18:11:19.117Z"), created_at_lte=parse_datetime("2019-12-27T18:11:19.117Z"), deployment_id="deployment_id", has_error=True, limit=0, page="page", trigger_type="schedule", betas=["message-batches-2024-09-24"], ) assert_matches_type(AsyncPageCursor[BetaManagedAgentsDeploymentRun], deployment_run, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_raw_response_list(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.deployment_runs.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment_run = response.parse() assert_matches_type(AsyncPageCursor[BetaManagedAgentsDeploymentRun], deployment_run, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_streaming_response_list(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.deployment_runs.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment_run = await response.parse() assert_matches_type(AsyncPageCursor[BetaManagedAgentsDeploymentRun], deployment_run, path=["response"]) assert cast(Any, response.is_closed) is True anthropic-sdk-python-0.120.2/tests/api_resources/beta/test_deployments.py000066400000000000000000001160701523216435200266450ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os from typing import Any, cast import pytest from anthropic import Anthropic, AsyncAnthropic from tests.utils import assert_matches_type from anthropic._utils import parse_datetime from anthropic.pagination import SyncPageCursor, AsyncPageCursor from anthropic.types.beta import ( BetaManagedAgentsDeployment, BetaManagedAgentsDeploymentRun, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestDeployments: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create(self, client: Anthropic) -> None: deployment = client.beta.deployments.create( agent="string", environment_id="x", initial_events=[ { "content": [ { "text": "Where is my order #1234?", "type": "text", } ], "type": "user.message", } ], name="x", ) assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @parametrize def test_method_create_with_all_params(self, client: Anthropic) -> None: deployment = client.beta.deployments.create( agent="string", environment_id="x", initial_events=[ { "content": [ { "text": "Where is my order #1234?", "type": "text", } ], "type": "user.message", } ], name="x", description="description", metadata={"foo": "string"}, resources=[ { "file_id": "file_011CNha8iCJcU1wXNR6q4V8w", "type": "file", "mount_path": "/uploads/receipt.pdf", } ], schedule={ "expression": "0 9 * * 1-5", "timezone": "America/Los_Angeles", "type": "cron", }, vault_ids=["string"], betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @parametrize def test_raw_response_create(self, client: Anthropic) -> None: response = client.beta.deployments.with_raw_response.create( agent="string", environment_id="x", initial_events=[ { "content": [ { "text": "Where is my order #1234?", "type": "text", } ], "type": "user.message", } ], name="x", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = response.parse() assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @parametrize def test_streaming_response_create(self, client: Anthropic) -> None: with client.beta.deployments.with_streaming_response.create( agent="string", environment_id="x", initial_events=[ { "content": [ { "text": "Where is my order #1234?", "type": "text", } ], "type": "user.message", } ], name="x", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = response.parse() assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_retrieve(self, client: Anthropic) -> None: deployment = client.beta.deployments.retrieve( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_retrieve_with_all_params(self, client: Anthropic) -> None: deployment = client.beta.deployments.retrieve( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_raw_response_retrieve(self, client: Anthropic) -> None: response = client.beta.deployments.with_raw_response.retrieve( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = response.parse() assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_streaming_response_retrieve(self, client: Anthropic) -> None: with client.beta.deployments.with_streaming_response.retrieve( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = response.parse() assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_path_params_retrieve(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `deployment_id` but received ''"): client.beta.deployments.with_raw_response.retrieve( deployment_id="", ) @parametrize def test_method_update(self, client: Anthropic) -> None: deployment = client.beta.deployments.update( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @parametrize def test_method_update_with_all_params(self, client: Anthropic) -> None: deployment = client.beta.deployments.update( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", agent="string", description="description", environment_id="environment_id", initial_events=[ { "content": [ { "text": "Where is my order #1234?", "type": "text", } ], "type": "user.message", } ], metadata={"foo": "string"}, name="name", resources=[ { "file_id": "file_011CNha8iCJcU1wXNR6q4V8w", "type": "file", "mount_path": "/uploads/receipt.pdf", } ], schedule={ "expression": "0 9 * * 1-5", "timezone": "America/Los_Angeles", "type": "cron", }, vault_ids=["string"], betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @parametrize def test_raw_response_update(self, client: Anthropic) -> None: response = client.beta.deployments.with_raw_response.update( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = response.parse() assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @parametrize def test_streaming_response_update(self, client: Anthropic) -> None: with client.beta.deployments.with_streaming_response.update( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = response.parse() assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_update(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `deployment_id` but received ''"): client.beta.deployments.with_raw_response.update( deployment_id="", ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list(self, client: Anthropic) -> None: deployment = client.beta.deployments.list() assert_matches_type(SyncPageCursor[BetaManagedAgentsDeployment], deployment, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list_with_all_params(self, client: Anthropic) -> None: deployment = client.beta.deployments.list( agent_id="agent_id", created_at_gte=parse_datetime("2019-12-27T18:11:19.117Z"), created_at_lte=parse_datetime("2019-12-27T18:11:19.117Z"), include_archived=True, limit=0, page="page", status="active", betas=["message-batches-2024-09-24"], ) assert_matches_type(SyncPageCursor[BetaManagedAgentsDeployment], deployment, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_raw_response_list(self, client: Anthropic) -> None: response = client.beta.deployments.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = response.parse() assert_matches_type(SyncPageCursor[BetaManagedAgentsDeployment], deployment, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_streaming_response_list(self, client: Anthropic) -> None: with client.beta.deployments.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = response.parse() assert_matches_type(SyncPageCursor[BetaManagedAgentsDeployment], deployment, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_archive(self, client: Anthropic) -> None: deployment = client.beta.deployments.archive( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @parametrize def test_method_archive_with_all_params(self, client: Anthropic) -> None: deployment = client.beta.deployments.archive( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @parametrize def test_raw_response_archive(self, client: Anthropic) -> None: response = client.beta.deployments.with_raw_response.archive( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = response.parse() assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @parametrize def test_streaming_response_archive(self, client: Anthropic) -> None: with client.beta.deployments.with_streaming_response.archive( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = response.parse() assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_archive(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `deployment_id` but received ''"): client.beta.deployments.with_raw_response.archive( deployment_id="", ) @parametrize def test_method_pause(self, client: Anthropic) -> None: deployment = client.beta.deployments.pause( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @parametrize def test_method_pause_with_all_params(self, client: Anthropic) -> None: deployment = client.beta.deployments.pause( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @parametrize def test_raw_response_pause(self, client: Anthropic) -> None: response = client.beta.deployments.with_raw_response.pause( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = response.parse() assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @parametrize def test_streaming_response_pause(self, client: Anthropic) -> None: with client.beta.deployments.with_streaming_response.pause( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = response.parse() assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_pause(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `deployment_id` but received ''"): client.beta.deployments.with_raw_response.pause( deployment_id="", ) @parametrize def test_method_run(self, client: Anthropic) -> None: deployment = client.beta.deployments.run( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) assert_matches_type(BetaManagedAgentsDeploymentRun, deployment, path=["response"]) @parametrize def test_method_run_with_all_params(self, client: Anthropic) -> None: deployment = client.beta.deployments.run( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsDeploymentRun, deployment, path=["response"]) @parametrize def test_raw_response_run(self, client: Anthropic) -> None: response = client.beta.deployments.with_raw_response.run( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = response.parse() assert_matches_type(BetaManagedAgentsDeploymentRun, deployment, path=["response"]) @parametrize def test_streaming_response_run(self, client: Anthropic) -> None: with client.beta.deployments.with_streaming_response.run( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = response.parse() assert_matches_type(BetaManagedAgentsDeploymentRun, deployment, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_run(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `deployment_id` but received ''"): client.beta.deployments.with_raw_response.run( deployment_id="", ) @parametrize def test_method_unpause(self, client: Anthropic) -> None: deployment = client.beta.deployments.unpause( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @parametrize def test_method_unpause_with_all_params(self, client: Anthropic) -> None: deployment = client.beta.deployments.unpause( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @parametrize def test_raw_response_unpause(self, client: Anthropic) -> None: response = client.beta.deployments.with_raw_response.unpause( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = response.parse() assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @parametrize def test_streaming_response_unpause(self, client: Anthropic) -> None: with client.beta.deployments.with_streaming_response.unpause( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = response.parse() assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_unpause(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `deployment_id` but received ''"): client.beta.deployments.with_raw_response.unpause( deployment_id="", ) class TestAsyncDeployments: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize async def test_method_create(self, async_client: AsyncAnthropic) -> None: deployment = await async_client.beta.deployments.create( agent="string", environment_id="x", initial_events=[ { "content": [ { "text": "Where is my order #1234?", "type": "text", } ], "type": "user.message", } ], name="x", ) assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @parametrize async def test_method_create_with_all_params(self, async_client: AsyncAnthropic) -> None: deployment = await async_client.beta.deployments.create( agent="string", environment_id="x", initial_events=[ { "content": [ { "text": "Where is my order #1234?", "type": "text", } ], "type": "user.message", } ], name="x", description="description", metadata={"foo": "string"}, resources=[ { "file_id": "file_011CNha8iCJcU1wXNR6q4V8w", "type": "file", "mount_path": "/uploads/receipt.pdf", } ], schedule={ "expression": "0 9 * * 1-5", "timezone": "America/Los_Angeles", "type": "cron", }, vault_ids=["string"], betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @parametrize async def test_raw_response_create(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.deployments.with_raw_response.create( agent="string", environment_id="x", initial_events=[ { "content": [ { "text": "Where is my order #1234?", "type": "text", } ], "type": "user.message", } ], name="x", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = response.parse() assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @parametrize async def test_streaming_response_create(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.deployments.with_streaming_response.create( agent="string", environment_id="x", initial_events=[ { "content": [ { "text": "Where is my order #1234?", "type": "text", } ], "type": "user.message", } ], name="x", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = await response.parse() assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_retrieve(self, async_client: AsyncAnthropic) -> None: deployment = await async_client.beta.deployments.retrieve( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_retrieve_with_all_params(self, async_client: AsyncAnthropic) -> None: deployment = await async_client.beta.deployments.retrieve( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.deployments.with_raw_response.retrieve( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = response.parse() assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.deployments.with_streaming_response.retrieve( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = await response.parse() assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_path_params_retrieve(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `deployment_id` but received ''"): await async_client.beta.deployments.with_raw_response.retrieve( deployment_id="", ) @parametrize async def test_method_update(self, async_client: AsyncAnthropic) -> None: deployment = await async_client.beta.deployments.update( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @parametrize async def test_method_update_with_all_params(self, async_client: AsyncAnthropic) -> None: deployment = await async_client.beta.deployments.update( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", agent="string", description="description", environment_id="environment_id", initial_events=[ { "content": [ { "text": "Where is my order #1234?", "type": "text", } ], "type": "user.message", } ], metadata={"foo": "string"}, name="name", resources=[ { "file_id": "file_011CNha8iCJcU1wXNR6q4V8w", "type": "file", "mount_path": "/uploads/receipt.pdf", } ], schedule={ "expression": "0 9 * * 1-5", "timezone": "America/Los_Angeles", "type": "cron", }, vault_ids=["string"], betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @parametrize async def test_raw_response_update(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.deployments.with_raw_response.update( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = response.parse() assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @parametrize async def test_streaming_response_update(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.deployments.with_streaming_response.update( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = await response.parse() assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_update(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `deployment_id` but received ''"): await async_client.beta.deployments.with_raw_response.update( deployment_id="", ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list(self, async_client: AsyncAnthropic) -> None: deployment = await async_client.beta.deployments.list() assert_matches_type(AsyncPageCursor[BetaManagedAgentsDeployment], deployment, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncAnthropic) -> None: deployment = await async_client.beta.deployments.list( agent_id="agent_id", created_at_gte=parse_datetime("2019-12-27T18:11:19.117Z"), created_at_lte=parse_datetime("2019-12-27T18:11:19.117Z"), include_archived=True, limit=0, page="page", status="active", betas=["message-batches-2024-09-24"], ) assert_matches_type(AsyncPageCursor[BetaManagedAgentsDeployment], deployment, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_raw_response_list(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.deployments.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = response.parse() assert_matches_type(AsyncPageCursor[BetaManagedAgentsDeployment], deployment, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_streaming_response_list(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.deployments.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = await response.parse() assert_matches_type(AsyncPageCursor[BetaManagedAgentsDeployment], deployment, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_archive(self, async_client: AsyncAnthropic) -> None: deployment = await async_client.beta.deployments.archive( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @parametrize async def test_method_archive_with_all_params(self, async_client: AsyncAnthropic) -> None: deployment = await async_client.beta.deployments.archive( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @parametrize async def test_raw_response_archive(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.deployments.with_raw_response.archive( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = response.parse() assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @parametrize async def test_streaming_response_archive(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.deployments.with_streaming_response.archive( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = await response.parse() assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_archive(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `deployment_id` but received ''"): await async_client.beta.deployments.with_raw_response.archive( deployment_id="", ) @parametrize async def test_method_pause(self, async_client: AsyncAnthropic) -> None: deployment = await async_client.beta.deployments.pause( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @parametrize async def test_method_pause_with_all_params(self, async_client: AsyncAnthropic) -> None: deployment = await async_client.beta.deployments.pause( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @parametrize async def test_raw_response_pause(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.deployments.with_raw_response.pause( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = response.parse() assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @parametrize async def test_streaming_response_pause(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.deployments.with_streaming_response.pause( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = await response.parse() assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_pause(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `deployment_id` but received ''"): await async_client.beta.deployments.with_raw_response.pause( deployment_id="", ) @parametrize async def test_method_run(self, async_client: AsyncAnthropic) -> None: deployment = await async_client.beta.deployments.run( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) assert_matches_type(BetaManagedAgentsDeploymentRun, deployment, path=["response"]) @parametrize async def test_method_run_with_all_params(self, async_client: AsyncAnthropic) -> None: deployment = await async_client.beta.deployments.run( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsDeploymentRun, deployment, path=["response"]) @parametrize async def test_raw_response_run(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.deployments.with_raw_response.run( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = response.parse() assert_matches_type(BetaManagedAgentsDeploymentRun, deployment, path=["response"]) @parametrize async def test_streaming_response_run(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.deployments.with_streaming_response.run( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = await response.parse() assert_matches_type(BetaManagedAgentsDeploymentRun, deployment, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_run(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `deployment_id` but received ''"): await async_client.beta.deployments.with_raw_response.run( deployment_id="", ) @parametrize async def test_method_unpause(self, async_client: AsyncAnthropic) -> None: deployment = await async_client.beta.deployments.unpause( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @parametrize async def test_method_unpause_with_all_params(self, async_client: AsyncAnthropic) -> None: deployment = await async_client.beta.deployments.unpause( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @parametrize async def test_raw_response_unpause(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.deployments.with_raw_response.unpause( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = response.parse() assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) @parametrize async def test_streaming_response_unpause(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.deployments.with_streaming_response.unpause( deployment_id="depl_011CZkZcDH3vPqd7xnEfwTai", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" deployment = await response.parse() assert_matches_type(BetaManagedAgentsDeployment, deployment, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_unpause(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `deployment_id` but received ''"): await async_client.beta.deployments.with_raw_response.unpause( deployment_id="", ) anthropic-sdk-python-0.120.2/tests/api_resources/beta/test_dreams.py000066400000000000000000000455501523216435200255610ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os from typing import Any, cast import pytest from anthropic import Anthropic, AsyncAnthropic from tests.utils import assert_matches_type from anthropic._utils import parse_datetime from anthropic.pagination import SyncPageCursor, AsyncPageCursor from anthropic.types.beta import BetaDream base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestDreams: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create(self, client: Anthropic) -> None: dream = client.beta.dreams.create( inputs=[ { "memory_store_id": "x", "type": "memory_store", } ], model="string", ) assert_matches_type(BetaDream, dream, path=["response"]) @parametrize def test_method_create_with_all_params(self, client: Anthropic) -> None: dream = client.beta.dreams.create( inputs=[ { "memory_store_id": "x", "type": "memory_store", } ], model="string", instructions="x", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaDream, dream, path=["response"]) @parametrize def test_raw_response_create(self, client: Anthropic) -> None: response = client.beta.dreams.with_raw_response.create( inputs=[ { "memory_store_id": "x", "type": "memory_store", } ], model="string", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" dream = response.parse() assert_matches_type(BetaDream, dream, path=["response"]) @parametrize def test_streaming_response_create(self, client: Anthropic) -> None: with client.beta.dreams.with_streaming_response.create( inputs=[ { "memory_store_id": "x", "type": "memory_store", } ], model="string", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" dream = response.parse() assert_matches_type(BetaDream, dream, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_retrieve(self, client: Anthropic) -> None: dream = client.beta.dreams.retrieve( dream_id="dream_id", ) assert_matches_type(BetaDream, dream, path=["response"]) @parametrize def test_method_retrieve_with_all_params(self, client: Anthropic) -> None: dream = client.beta.dreams.retrieve( dream_id="dream_id", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaDream, dream, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: Anthropic) -> None: response = client.beta.dreams.with_raw_response.retrieve( dream_id="dream_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" dream = response.parse() assert_matches_type(BetaDream, dream, path=["response"]) @parametrize def test_streaming_response_retrieve(self, client: Anthropic) -> None: with client.beta.dreams.with_streaming_response.retrieve( dream_id="dream_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" dream = response.parse() assert_matches_type(BetaDream, dream, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_retrieve(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `dream_id` but received ''"): client.beta.dreams.with_raw_response.retrieve( dream_id="", ) @parametrize def test_method_list(self, client: Anthropic) -> None: dream = client.beta.dreams.list() assert_matches_type(SyncPageCursor[BetaDream], dream, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: Anthropic) -> None: dream = client.beta.dreams.list( created_at_gt=parse_datetime("2019-12-27T18:11:19.117Z"), created_at_lt=parse_datetime("2019-12-27T18:11:19.117Z"), include_archived=True, limit=0, page="page", statuses=["pending"], betas=["message-batches-2024-09-24"], ) assert_matches_type(SyncPageCursor[BetaDream], dream, path=["response"]) @parametrize def test_raw_response_list(self, client: Anthropic) -> None: response = client.beta.dreams.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" dream = response.parse() assert_matches_type(SyncPageCursor[BetaDream], dream, path=["response"]) @parametrize def test_streaming_response_list(self, client: Anthropic) -> None: with client.beta.dreams.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" dream = response.parse() assert_matches_type(SyncPageCursor[BetaDream], dream, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_archive(self, client: Anthropic) -> None: dream = client.beta.dreams.archive( dream_id="dream_id", ) assert_matches_type(BetaDream, dream, path=["response"]) @parametrize def test_method_archive_with_all_params(self, client: Anthropic) -> None: dream = client.beta.dreams.archive( dream_id="dream_id", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaDream, dream, path=["response"]) @parametrize def test_raw_response_archive(self, client: Anthropic) -> None: response = client.beta.dreams.with_raw_response.archive( dream_id="dream_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" dream = response.parse() assert_matches_type(BetaDream, dream, path=["response"]) @parametrize def test_streaming_response_archive(self, client: Anthropic) -> None: with client.beta.dreams.with_streaming_response.archive( dream_id="dream_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" dream = response.parse() assert_matches_type(BetaDream, dream, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_archive(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `dream_id` but received ''"): client.beta.dreams.with_raw_response.archive( dream_id="", ) @parametrize def test_method_cancel(self, client: Anthropic) -> None: dream = client.beta.dreams.cancel( dream_id="dream_id", ) assert_matches_type(BetaDream, dream, path=["response"]) @parametrize def test_method_cancel_with_all_params(self, client: Anthropic) -> None: dream = client.beta.dreams.cancel( dream_id="dream_id", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaDream, dream, path=["response"]) @parametrize def test_raw_response_cancel(self, client: Anthropic) -> None: response = client.beta.dreams.with_raw_response.cancel( dream_id="dream_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" dream = response.parse() assert_matches_type(BetaDream, dream, path=["response"]) @parametrize def test_streaming_response_cancel(self, client: Anthropic) -> None: with client.beta.dreams.with_streaming_response.cancel( dream_id="dream_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" dream = response.parse() assert_matches_type(BetaDream, dream, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_cancel(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `dream_id` but received ''"): client.beta.dreams.with_raw_response.cancel( dream_id="", ) class TestAsyncDreams: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize async def test_method_create(self, async_client: AsyncAnthropic) -> None: dream = await async_client.beta.dreams.create( inputs=[ { "memory_store_id": "x", "type": "memory_store", } ], model="string", ) assert_matches_type(BetaDream, dream, path=["response"]) @parametrize async def test_method_create_with_all_params(self, async_client: AsyncAnthropic) -> None: dream = await async_client.beta.dreams.create( inputs=[ { "memory_store_id": "x", "type": "memory_store", } ], model="string", instructions="x", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaDream, dream, path=["response"]) @parametrize async def test_raw_response_create(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.dreams.with_raw_response.create( inputs=[ { "memory_store_id": "x", "type": "memory_store", } ], model="string", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" dream = response.parse() assert_matches_type(BetaDream, dream, path=["response"]) @parametrize async def test_streaming_response_create(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.dreams.with_streaming_response.create( inputs=[ { "memory_store_id": "x", "type": "memory_store", } ], model="string", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" dream = await response.parse() assert_matches_type(BetaDream, dream, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_retrieve(self, async_client: AsyncAnthropic) -> None: dream = await async_client.beta.dreams.retrieve( dream_id="dream_id", ) assert_matches_type(BetaDream, dream, path=["response"]) @parametrize async def test_method_retrieve_with_all_params(self, async_client: AsyncAnthropic) -> None: dream = await async_client.beta.dreams.retrieve( dream_id="dream_id", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaDream, dream, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.dreams.with_raw_response.retrieve( dream_id="dream_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" dream = response.parse() assert_matches_type(BetaDream, dream, path=["response"]) @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.dreams.with_streaming_response.retrieve( dream_id="dream_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" dream = await response.parse() assert_matches_type(BetaDream, dream, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_retrieve(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `dream_id` but received ''"): await async_client.beta.dreams.with_raw_response.retrieve( dream_id="", ) @parametrize async def test_method_list(self, async_client: AsyncAnthropic) -> None: dream = await async_client.beta.dreams.list() assert_matches_type(AsyncPageCursor[BetaDream], dream, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncAnthropic) -> None: dream = await async_client.beta.dreams.list( created_at_gt=parse_datetime("2019-12-27T18:11:19.117Z"), created_at_lt=parse_datetime("2019-12-27T18:11:19.117Z"), include_archived=True, limit=0, page="page", statuses=["pending"], betas=["message-batches-2024-09-24"], ) assert_matches_type(AsyncPageCursor[BetaDream], dream, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.dreams.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" dream = response.parse() assert_matches_type(AsyncPageCursor[BetaDream], dream, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.dreams.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" dream = await response.parse() assert_matches_type(AsyncPageCursor[BetaDream], dream, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_archive(self, async_client: AsyncAnthropic) -> None: dream = await async_client.beta.dreams.archive( dream_id="dream_id", ) assert_matches_type(BetaDream, dream, path=["response"]) @parametrize async def test_method_archive_with_all_params(self, async_client: AsyncAnthropic) -> None: dream = await async_client.beta.dreams.archive( dream_id="dream_id", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaDream, dream, path=["response"]) @parametrize async def test_raw_response_archive(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.dreams.with_raw_response.archive( dream_id="dream_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" dream = response.parse() assert_matches_type(BetaDream, dream, path=["response"]) @parametrize async def test_streaming_response_archive(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.dreams.with_streaming_response.archive( dream_id="dream_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" dream = await response.parse() assert_matches_type(BetaDream, dream, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_archive(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `dream_id` but received ''"): await async_client.beta.dreams.with_raw_response.archive( dream_id="", ) @parametrize async def test_method_cancel(self, async_client: AsyncAnthropic) -> None: dream = await async_client.beta.dreams.cancel( dream_id="dream_id", ) assert_matches_type(BetaDream, dream, path=["response"]) @parametrize async def test_method_cancel_with_all_params(self, async_client: AsyncAnthropic) -> None: dream = await async_client.beta.dreams.cancel( dream_id="dream_id", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaDream, dream, path=["response"]) @parametrize async def test_raw_response_cancel(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.dreams.with_raw_response.cancel( dream_id="dream_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" dream = response.parse() assert_matches_type(BetaDream, dream, path=["response"]) @parametrize async def test_streaming_response_cancel(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.dreams.with_streaming_response.cancel( dream_id="dream_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" dream = await response.parse() assert_matches_type(BetaDream, dream, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_cancel(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `dream_id` but received ''"): await async_client.beta.dreams.with_raw_response.cancel( dream_id="", ) anthropic-sdk-python-0.120.2/tests/api_resources/beta/test_environments.py000066400000000000000000000637331523216435200270400ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os from typing import Any, cast import pytest from anthropic import Anthropic, AsyncAnthropic from tests.utils import assert_matches_type from anthropic.pagination import SyncPageCursor, AsyncPageCursor from anthropic.types.beta import ( BetaEnvironment, BetaEnvironmentDeleteResponse, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestEnvironments: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create(self, client: Anthropic) -> None: environment = client.beta.environments.create( name="python-data-analysis", ) assert_matches_type(BetaEnvironment, environment, path=["response"]) @parametrize def test_method_create_with_all_params(self, client: Anthropic) -> None: environment = client.beta.environments.create( name="python-data-analysis", config={ "type": "cloud", "networking": { "type": "limited", "allow_mcp_servers": True, "allow_package_managers": True, "allowed_hosts": ["api.example.com"], }, "packages": { "apt": ["string"], "cargo": ["string"], "gem": ["string"], "go": ["string"], "npm": ["string"], "pip": ["pandas", "numpy"], "type": "packages", }, }, description="Python environment with data-analysis packages.", metadata={"foo": "string"}, scope="organization", betas=["string"], ) assert_matches_type(BetaEnvironment, environment, path=["response"]) @parametrize def test_raw_response_create(self, client: Anthropic) -> None: response = client.beta.environments.with_raw_response.create( name="python-data-analysis", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" environment = response.parse() assert_matches_type(BetaEnvironment, environment, path=["response"]) @parametrize def test_streaming_response_create(self, client: Anthropic) -> None: with client.beta.environments.with_streaming_response.create( name="python-data-analysis", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" environment = response.parse() assert_matches_type(BetaEnvironment, environment, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_retrieve(self, client: Anthropic) -> None: environment = client.beta.environments.retrieve( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert_matches_type(BetaEnvironment, environment, path=["response"]) @parametrize def test_method_retrieve_with_all_params(self, client: Anthropic) -> None: environment = client.beta.environments.retrieve( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", betas=["string"], ) assert_matches_type(BetaEnvironment, environment, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: Anthropic) -> None: response = client.beta.environments.with_raw_response.retrieve( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" environment = response.parse() assert_matches_type(BetaEnvironment, environment, path=["response"]) @parametrize def test_streaming_response_retrieve(self, client: Anthropic) -> None: with client.beta.environments.with_streaming_response.retrieve( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" environment = response.parse() assert_matches_type(BetaEnvironment, environment, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_retrieve(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"): client.beta.environments.with_raw_response.retrieve( environment_id="", ) @parametrize def test_method_update(self, client: Anthropic) -> None: environment = client.beta.environments.update( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert_matches_type(BetaEnvironment, environment, path=["response"]) @parametrize def test_method_update_with_all_params(self, client: Anthropic) -> None: environment = client.beta.environments.update( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", config={ "type": "cloud", "networking": { "type": "limited", "allow_mcp_servers": True, "allow_package_managers": True, "allowed_hosts": ["api.example.com"], }, "packages": { "apt": ["string"], "cargo": ["string"], "gem": ["string"], "go": ["string"], "npm": ["string"], "pip": ["pandas", "numpy"], "type": "packages", }, }, description="Python environment with data-analysis packages.", metadata={"foo": "string"}, name="x", scope="organization", betas=["string"], ) assert_matches_type(BetaEnvironment, environment, path=["response"]) @parametrize def test_raw_response_update(self, client: Anthropic) -> None: response = client.beta.environments.with_raw_response.update( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" environment = response.parse() assert_matches_type(BetaEnvironment, environment, path=["response"]) @parametrize def test_streaming_response_update(self, client: Anthropic) -> None: with client.beta.environments.with_streaming_response.update( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" environment = response.parse() assert_matches_type(BetaEnvironment, environment, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_update(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"): client.beta.environments.with_raw_response.update( environment_id="", ) @parametrize def test_method_list(self, client: Anthropic) -> None: environment = client.beta.environments.list() assert_matches_type(SyncPageCursor[BetaEnvironment], environment, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: Anthropic) -> None: environment = client.beta.environments.list( include_archived=True, limit=1, page="page", betas=["string"], ) assert_matches_type(SyncPageCursor[BetaEnvironment], environment, path=["response"]) @parametrize def test_raw_response_list(self, client: Anthropic) -> None: response = client.beta.environments.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" environment = response.parse() assert_matches_type(SyncPageCursor[BetaEnvironment], environment, path=["response"]) @parametrize def test_streaming_response_list(self, client: Anthropic) -> None: with client.beta.environments.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" environment = response.parse() assert_matches_type(SyncPageCursor[BetaEnvironment], environment, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_delete(self, client: Anthropic) -> None: environment = client.beta.environments.delete( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert_matches_type(BetaEnvironmentDeleteResponse, environment, path=["response"]) @parametrize def test_method_delete_with_all_params(self, client: Anthropic) -> None: environment = client.beta.environments.delete( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", betas=["string"], ) assert_matches_type(BetaEnvironmentDeleteResponse, environment, path=["response"]) @parametrize def test_raw_response_delete(self, client: Anthropic) -> None: response = client.beta.environments.with_raw_response.delete( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" environment = response.parse() assert_matches_type(BetaEnvironmentDeleteResponse, environment, path=["response"]) @parametrize def test_streaming_response_delete(self, client: Anthropic) -> None: with client.beta.environments.with_streaming_response.delete( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" environment = response.parse() assert_matches_type(BetaEnvironmentDeleteResponse, environment, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_delete(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"): client.beta.environments.with_raw_response.delete( environment_id="", ) @parametrize def test_method_archive(self, client: Anthropic) -> None: environment = client.beta.environments.archive( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert_matches_type(BetaEnvironment, environment, path=["response"]) @parametrize def test_method_archive_with_all_params(self, client: Anthropic) -> None: environment = client.beta.environments.archive( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", betas=["string"], ) assert_matches_type(BetaEnvironment, environment, path=["response"]) @parametrize def test_raw_response_archive(self, client: Anthropic) -> None: response = client.beta.environments.with_raw_response.archive( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" environment = response.parse() assert_matches_type(BetaEnvironment, environment, path=["response"]) @parametrize def test_streaming_response_archive(self, client: Anthropic) -> None: with client.beta.environments.with_streaming_response.archive( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" environment = response.parse() assert_matches_type(BetaEnvironment, environment, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_archive(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"): client.beta.environments.with_raw_response.archive( environment_id="", ) class TestAsyncEnvironments: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize async def test_method_create(self, async_client: AsyncAnthropic) -> None: environment = await async_client.beta.environments.create( name="python-data-analysis", ) assert_matches_type(BetaEnvironment, environment, path=["response"]) @parametrize async def test_method_create_with_all_params(self, async_client: AsyncAnthropic) -> None: environment = await async_client.beta.environments.create( name="python-data-analysis", config={ "type": "cloud", "networking": { "type": "limited", "allow_mcp_servers": True, "allow_package_managers": True, "allowed_hosts": ["api.example.com"], }, "packages": { "apt": ["string"], "cargo": ["string"], "gem": ["string"], "go": ["string"], "npm": ["string"], "pip": ["pandas", "numpy"], "type": "packages", }, }, description="Python environment with data-analysis packages.", metadata={"foo": "string"}, scope="organization", betas=["string"], ) assert_matches_type(BetaEnvironment, environment, path=["response"]) @parametrize async def test_raw_response_create(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.environments.with_raw_response.create( name="python-data-analysis", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" environment = response.parse() assert_matches_type(BetaEnvironment, environment, path=["response"]) @parametrize async def test_streaming_response_create(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.environments.with_streaming_response.create( name="python-data-analysis", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" environment = await response.parse() assert_matches_type(BetaEnvironment, environment, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_retrieve(self, async_client: AsyncAnthropic) -> None: environment = await async_client.beta.environments.retrieve( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert_matches_type(BetaEnvironment, environment, path=["response"]) @parametrize async def test_method_retrieve_with_all_params(self, async_client: AsyncAnthropic) -> None: environment = await async_client.beta.environments.retrieve( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", betas=["string"], ) assert_matches_type(BetaEnvironment, environment, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.environments.with_raw_response.retrieve( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" environment = response.parse() assert_matches_type(BetaEnvironment, environment, path=["response"]) @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.environments.with_streaming_response.retrieve( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" environment = await response.parse() assert_matches_type(BetaEnvironment, environment, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_retrieve(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"): await async_client.beta.environments.with_raw_response.retrieve( environment_id="", ) @parametrize async def test_method_update(self, async_client: AsyncAnthropic) -> None: environment = await async_client.beta.environments.update( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert_matches_type(BetaEnvironment, environment, path=["response"]) @parametrize async def test_method_update_with_all_params(self, async_client: AsyncAnthropic) -> None: environment = await async_client.beta.environments.update( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", config={ "type": "cloud", "networking": { "type": "limited", "allow_mcp_servers": True, "allow_package_managers": True, "allowed_hosts": ["api.example.com"], }, "packages": { "apt": ["string"], "cargo": ["string"], "gem": ["string"], "go": ["string"], "npm": ["string"], "pip": ["pandas", "numpy"], "type": "packages", }, }, description="Python environment with data-analysis packages.", metadata={"foo": "string"}, name="x", scope="organization", betas=["string"], ) assert_matches_type(BetaEnvironment, environment, path=["response"]) @parametrize async def test_raw_response_update(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.environments.with_raw_response.update( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" environment = response.parse() assert_matches_type(BetaEnvironment, environment, path=["response"]) @parametrize async def test_streaming_response_update(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.environments.with_streaming_response.update( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" environment = await response.parse() assert_matches_type(BetaEnvironment, environment, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_update(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"): await async_client.beta.environments.with_raw_response.update( environment_id="", ) @parametrize async def test_method_list(self, async_client: AsyncAnthropic) -> None: environment = await async_client.beta.environments.list() assert_matches_type(AsyncPageCursor[BetaEnvironment], environment, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncAnthropic) -> None: environment = await async_client.beta.environments.list( include_archived=True, limit=1, page="page", betas=["string"], ) assert_matches_type(AsyncPageCursor[BetaEnvironment], environment, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.environments.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" environment = response.parse() assert_matches_type(AsyncPageCursor[BetaEnvironment], environment, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.environments.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" environment = await response.parse() assert_matches_type(AsyncPageCursor[BetaEnvironment], environment, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_delete(self, async_client: AsyncAnthropic) -> None: environment = await async_client.beta.environments.delete( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert_matches_type(BetaEnvironmentDeleteResponse, environment, path=["response"]) @parametrize async def test_method_delete_with_all_params(self, async_client: AsyncAnthropic) -> None: environment = await async_client.beta.environments.delete( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", betas=["string"], ) assert_matches_type(BetaEnvironmentDeleteResponse, environment, path=["response"]) @parametrize async def test_raw_response_delete(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.environments.with_raw_response.delete( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" environment = response.parse() assert_matches_type(BetaEnvironmentDeleteResponse, environment, path=["response"]) @parametrize async def test_streaming_response_delete(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.environments.with_streaming_response.delete( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" environment = await response.parse() assert_matches_type(BetaEnvironmentDeleteResponse, environment, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_delete(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"): await async_client.beta.environments.with_raw_response.delete( environment_id="", ) @parametrize async def test_method_archive(self, async_client: AsyncAnthropic) -> None: environment = await async_client.beta.environments.archive( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert_matches_type(BetaEnvironment, environment, path=["response"]) @parametrize async def test_method_archive_with_all_params(self, async_client: AsyncAnthropic) -> None: environment = await async_client.beta.environments.archive( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", betas=["string"], ) assert_matches_type(BetaEnvironment, environment, path=["response"]) @parametrize async def test_raw_response_archive(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.environments.with_raw_response.archive( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" environment = response.parse() assert_matches_type(BetaEnvironment, environment, path=["response"]) @parametrize async def test_streaming_response_archive(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.environments.with_streaming_response.archive( environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" environment = await response.parse() assert_matches_type(BetaEnvironment, environment, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_archive(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `environment_id` but received ''"): await async_client.beta.environments.with_raw_response.archive( environment_id="", ) anthropic-sdk-python-0.120.2/tests/api_resources/beta/test_files.py000066400000000000000000000472071523216435200254110ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os from typing import Any, cast import httpx import pytest from respx import MockRouter from anthropic import Anthropic, AsyncAnthropic from tests.utils import assert_matches_type from anthropic._response import ( BinaryAPIResponse, AsyncBinaryAPIResponse, StreamedBinaryAPIResponse, AsyncStreamedBinaryAPIResponse, ) from anthropic.pagination import SyncPage, AsyncPage from anthropic.types.beta import DeletedFile, FileMetadata # pyright: reportDeprecated=false base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestFiles: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_list(self, client: Anthropic) -> None: file = client.beta.files.list() assert_matches_type(SyncPage[FileMetadata], file, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: Anthropic) -> None: file = client.beta.files.list( after_id="after_id", before_id="before_id", limit=1, scope_id="scope_id", betas=["string"], ) assert_matches_type(SyncPage[FileMetadata], file, path=["response"]) @parametrize def test_raw_response_list(self, client: Anthropic) -> None: response = client.beta.files.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(SyncPage[FileMetadata], file, path=["response"]) @parametrize def test_streaming_response_list(self, client: Anthropic) -> None: with client.beta.files.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(SyncPage[FileMetadata], file, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_delete(self, client: Anthropic) -> None: file = client.beta.files.delete( file_id="file_id", ) assert_matches_type(DeletedFile, file, path=["response"]) @parametrize def test_method_delete_with_all_params(self, client: Anthropic) -> None: file = client.beta.files.delete( file_id="file_id", betas=["string"], ) assert_matches_type(DeletedFile, file, path=["response"]) @parametrize def test_raw_response_delete(self, client: Anthropic) -> None: response = client.beta.files.with_raw_response.delete( file_id="file_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(DeletedFile, file, path=["response"]) @parametrize def test_streaming_response_delete(self, client: Anthropic) -> None: with client.beta.files.with_streaming_response.delete( file_id="file_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(DeletedFile, file, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_delete(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): client.beta.files.with_raw_response.delete( file_id="", ) @parametrize @pytest.mark.respx(base_url=base_url) def test_method_download(self, client: Anthropic, respx_mock: MockRouter) -> None: respx_mock.get("/v1/files/file_id/content?beta=true").mock( return_value=httpx.Response(200, json={"foo": "bar"}) ) file = client.beta.files.download( file_id="file_id", ) assert file.is_closed assert file.json() == {"foo": "bar"} assert cast(Any, file.is_closed) is True assert isinstance(file, BinaryAPIResponse) @parametrize @pytest.mark.respx(base_url=base_url) def test_method_download_with_all_params(self, client: Anthropic, respx_mock: MockRouter) -> None: respx_mock.get("/v1/files/file_id/content?beta=true").mock( return_value=httpx.Response(200, json={"foo": "bar"}) ) file = client.beta.files.download( file_id="file_id", betas=["string"], ) assert file.is_closed assert file.json() == {"foo": "bar"} assert cast(Any, file.is_closed) is True assert isinstance(file, BinaryAPIResponse) @parametrize @pytest.mark.respx(base_url=base_url) def test_raw_response_download(self, client: Anthropic, respx_mock: MockRouter) -> None: respx_mock.get("/v1/files/file_id/content?beta=true").mock( return_value=httpx.Response(200, json={"foo": "bar"}) ) file = client.beta.files.with_raw_response.download( file_id="file_id", ) assert file.is_closed is True assert file.http_request.headers.get("X-Stainless-Lang") == "python" assert file.json() == {"foo": "bar"} assert isinstance(file, BinaryAPIResponse) @parametrize @pytest.mark.respx(base_url=base_url) def test_streaming_response_download(self, client: Anthropic, respx_mock: MockRouter) -> None: respx_mock.get("/v1/files/file_id/content?beta=true").mock( return_value=httpx.Response(200, json={"foo": "bar"}) ) with client.beta.files.with_streaming_response.download( file_id="file_id", ) as file: assert not file.is_closed assert file.http_request.headers.get("X-Stainless-Lang") == "python" assert file.json() == {"foo": "bar"} assert cast(Any, file.is_closed) is True assert isinstance(file, StreamedBinaryAPIResponse) assert cast(Any, file.is_closed) is True @parametrize @pytest.mark.respx(base_url=base_url) def test_path_params_download(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): client.beta.files.with_raw_response.download( file_id="", ) @parametrize def test_method_retrieve_metadata(self, client: Anthropic) -> None: file = client.beta.files.retrieve_metadata( file_id="file_id", ) assert_matches_type(FileMetadata, file, path=["response"]) @parametrize def test_method_retrieve_metadata_with_all_params(self, client: Anthropic) -> None: file = client.beta.files.retrieve_metadata( file_id="file_id", betas=["string"], ) assert_matches_type(FileMetadata, file, path=["response"]) @parametrize def test_raw_response_retrieve_metadata(self, client: Anthropic) -> None: response = client.beta.files.with_raw_response.retrieve_metadata( file_id="file_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(FileMetadata, file, path=["response"]) @parametrize def test_streaming_response_retrieve_metadata(self, client: Anthropic) -> None: with client.beta.files.with_streaming_response.retrieve_metadata( file_id="file_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(FileMetadata, file, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_retrieve_metadata(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): client.beta.files.with_raw_response.retrieve_metadata( file_id="", ) @parametrize def test_method_upload(self, client: Anthropic) -> None: file = client.beta.files.upload( file=b"Example data", ) assert_matches_type(FileMetadata, file, path=["response"]) @parametrize def test_method_upload_with_all_params(self, client: Anthropic) -> None: file = client.beta.files.upload( file=b"Example data", betas=["string"], ) assert_matches_type(FileMetadata, file, path=["response"]) @parametrize def test_raw_response_upload(self, client: Anthropic) -> None: response = client.beta.files.with_raw_response.upload( file=b"Example data", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(FileMetadata, file, path=["response"]) @parametrize def test_streaming_response_upload(self, client: Anthropic) -> None: with client.beta.files.with_streaming_response.upload( file=b"Example data", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(FileMetadata, file, path=["response"]) assert cast(Any, response.is_closed) is True class TestAsyncFiles: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize async def test_method_list(self, async_client: AsyncAnthropic) -> None: file = await async_client.beta.files.list() assert_matches_type(AsyncPage[FileMetadata], file, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncAnthropic) -> None: file = await async_client.beta.files.list( after_id="after_id", before_id="before_id", limit=1, scope_id="scope_id", betas=["string"], ) assert_matches_type(AsyncPage[FileMetadata], file, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.files.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(AsyncPage[FileMetadata], file, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.files.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = await response.parse() assert_matches_type(AsyncPage[FileMetadata], file, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_delete(self, async_client: AsyncAnthropic) -> None: file = await async_client.beta.files.delete( file_id="file_id", ) assert_matches_type(DeletedFile, file, path=["response"]) @parametrize async def test_method_delete_with_all_params(self, async_client: AsyncAnthropic) -> None: file = await async_client.beta.files.delete( file_id="file_id", betas=["string"], ) assert_matches_type(DeletedFile, file, path=["response"]) @parametrize async def test_raw_response_delete(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.files.with_raw_response.delete( file_id="file_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(DeletedFile, file, path=["response"]) @parametrize async def test_streaming_response_delete(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.files.with_streaming_response.delete( file_id="file_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = await response.parse() assert_matches_type(DeletedFile, file, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_delete(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): await async_client.beta.files.with_raw_response.delete( file_id="", ) @parametrize @pytest.mark.respx(base_url=base_url) async def test_method_download(self, async_client: AsyncAnthropic, respx_mock: MockRouter) -> None: respx_mock.get("/v1/files/file_id/content?beta=true").mock( return_value=httpx.Response(200, json={"foo": "bar"}) ) file = await async_client.beta.files.download( file_id="file_id", ) assert file.is_closed assert await file.json() == {"foo": "bar"} assert cast(Any, file.is_closed) is True assert isinstance(file, AsyncBinaryAPIResponse) @parametrize @pytest.mark.respx(base_url=base_url) async def test_method_download_with_all_params(self, async_client: AsyncAnthropic, respx_mock: MockRouter) -> None: respx_mock.get("/v1/files/file_id/content?beta=true").mock( return_value=httpx.Response(200, json={"foo": "bar"}) ) file = await async_client.beta.files.download( file_id="file_id", betas=["string"], ) assert file.is_closed assert await file.json() == {"foo": "bar"} assert cast(Any, file.is_closed) is True assert isinstance(file, AsyncBinaryAPIResponse) @parametrize @pytest.mark.respx(base_url=base_url) async def test_raw_response_download(self, async_client: AsyncAnthropic, respx_mock: MockRouter) -> None: respx_mock.get("/v1/files/file_id/content?beta=true").mock( return_value=httpx.Response(200, json={"foo": "bar"}) ) file = await async_client.beta.files.with_raw_response.download( file_id="file_id", ) assert file.is_closed is True assert file.http_request.headers.get("X-Stainless-Lang") == "python" assert await file.json() == {"foo": "bar"} assert isinstance(file, AsyncBinaryAPIResponse) @parametrize @pytest.mark.respx(base_url=base_url) async def test_streaming_response_download(self, async_client: AsyncAnthropic, respx_mock: MockRouter) -> None: respx_mock.get("/v1/files/file_id/content?beta=true").mock( return_value=httpx.Response(200, json={"foo": "bar"}) ) async with async_client.beta.files.with_streaming_response.download( file_id="file_id", ) as file: assert not file.is_closed assert file.http_request.headers.get("X-Stainless-Lang") == "python" assert await file.json() == {"foo": "bar"} assert cast(Any, file.is_closed) is True assert isinstance(file, AsyncStreamedBinaryAPIResponse) assert cast(Any, file.is_closed) is True @parametrize @pytest.mark.respx(base_url=base_url) async def test_path_params_download(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): await async_client.beta.files.with_raw_response.download( file_id="", ) @parametrize async def test_method_retrieve_metadata(self, async_client: AsyncAnthropic) -> None: file = await async_client.beta.files.retrieve_metadata( file_id="file_id", ) assert_matches_type(FileMetadata, file, path=["response"]) @parametrize async def test_method_retrieve_metadata_with_all_params(self, async_client: AsyncAnthropic) -> None: file = await async_client.beta.files.retrieve_metadata( file_id="file_id", betas=["string"], ) assert_matches_type(FileMetadata, file, path=["response"]) @parametrize async def test_raw_response_retrieve_metadata(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.files.with_raw_response.retrieve_metadata( file_id="file_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(FileMetadata, file, path=["response"]) @parametrize async def test_streaming_response_retrieve_metadata(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.files.with_streaming_response.retrieve_metadata( file_id="file_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = await response.parse() assert_matches_type(FileMetadata, file, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_retrieve_metadata(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): await async_client.beta.files.with_raw_response.retrieve_metadata( file_id="", ) @parametrize async def test_method_upload(self, async_client: AsyncAnthropic) -> None: file = await async_client.beta.files.upload( file=b"Example data", ) assert_matches_type(FileMetadata, file, path=["response"]) @parametrize async def test_method_upload_with_all_params(self, async_client: AsyncAnthropic) -> None: file = await async_client.beta.files.upload( file=b"Example data", betas=["string"], ) assert_matches_type(FileMetadata, file, path=["response"]) @parametrize async def test_raw_response_upload(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.files.with_raw_response.upload( file=b"Example data", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = response.parse() assert_matches_type(FileMetadata, file, path=["response"]) @parametrize async def test_streaming_response_upload(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.files.with_streaming_response.upload( file=b"Example data", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" file = await response.parse() assert_matches_type(FileMetadata, file, path=["response"]) assert cast(Any, response.is_closed) is True anthropic-sdk-python-0.120.2/tests/api_resources/beta/test_memory_stores.py000066400000000000000000000611021523216435200272040ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os from typing import Any, cast import pytest from anthropic import Anthropic, AsyncAnthropic from tests.utils import assert_matches_type from anthropic._utils import parse_datetime from anthropic.pagination import SyncPageCursor, AsyncPageCursor from anthropic.types.beta import ( BetaManagedAgentsMemoryStore, BetaManagedAgentsDeletedMemoryStore, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestMemoryStores: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create(self, client: Anthropic) -> None: memory_store = client.beta.memory_stores.create( name="x", ) assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) @parametrize def test_method_create_with_all_params(self, client: Anthropic) -> None: memory_store = client.beta.memory_stores.create( name="x", description="description", metadata={"foo": "string"}, betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) @parametrize def test_raw_response_create(self, client: Anthropic) -> None: response = client.beta.memory_stores.with_raw_response.create( name="x", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_store = response.parse() assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) @parametrize def test_streaming_response_create(self, client: Anthropic) -> None: with client.beta.memory_stores.with_streaming_response.create( name="x", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_store = response.parse() assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_retrieve(self, client: Anthropic) -> None: memory_store = client.beta.memory_stores.retrieve( memory_store_id="memory_store_id", ) assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) @parametrize def test_method_retrieve_with_all_params(self, client: Anthropic) -> None: memory_store = client.beta.memory_stores.retrieve( memory_store_id="memory_store_id", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: Anthropic) -> None: response = client.beta.memory_stores.with_raw_response.retrieve( memory_store_id="memory_store_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_store = response.parse() assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) @parametrize def test_streaming_response_retrieve(self, client: Anthropic) -> None: with client.beta.memory_stores.with_streaming_response.retrieve( memory_store_id="memory_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_store = response.parse() assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_retrieve(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_store_id` but received ''"): client.beta.memory_stores.with_raw_response.retrieve( memory_store_id="", ) @parametrize def test_method_update(self, client: Anthropic) -> None: memory_store = client.beta.memory_stores.update( memory_store_id="memory_store_id", ) assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) @parametrize def test_method_update_with_all_params(self, client: Anthropic) -> None: memory_store = client.beta.memory_stores.update( memory_store_id="memory_store_id", description="description", metadata={"foo": "string"}, name="x", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) @parametrize def test_raw_response_update(self, client: Anthropic) -> None: response = client.beta.memory_stores.with_raw_response.update( memory_store_id="memory_store_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_store = response.parse() assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) @parametrize def test_streaming_response_update(self, client: Anthropic) -> None: with client.beta.memory_stores.with_streaming_response.update( memory_store_id="memory_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_store = response.parse() assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_update(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_store_id` but received ''"): client.beta.memory_stores.with_raw_response.update( memory_store_id="", ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list(self, client: Anthropic) -> None: memory_store = client.beta.memory_stores.list() assert_matches_type(SyncPageCursor[BetaManagedAgentsMemoryStore], memory_store, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list_with_all_params(self, client: Anthropic) -> None: memory_store = client.beta.memory_stores.list( created_at_gte=parse_datetime("2019-12-27T18:11:19.117Z"), created_at_lte=parse_datetime("2019-12-27T18:11:19.117Z"), include_archived=True, limit=0, page="page", betas=["message-batches-2024-09-24"], ) assert_matches_type(SyncPageCursor[BetaManagedAgentsMemoryStore], memory_store, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_raw_response_list(self, client: Anthropic) -> None: response = client.beta.memory_stores.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_store = response.parse() assert_matches_type(SyncPageCursor[BetaManagedAgentsMemoryStore], memory_store, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_streaming_response_list(self, client: Anthropic) -> None: with client.beta.memory_stores.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_store = response.parse() assert_matches_type(SyncPageCursor[BetaManagedAgentsMemoryStore], memory_store, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_delete(self, client: Anthropic) -> None: memory_store = client.beta.memory_stores.delete( memory_store_id="memory_store_id", ) assert_matches_type(BetaManagedAgentsDeletedMemoryStore, memory_store, path=["response"]) @parametrize def test_method_delete_with_all_params(self, client: Anthropic) -> None: memory_store = client.beta.memory_stores.delete( memory_store_id="memory_store_id", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsDeletedMemoryStore, memory_store, path=["response"]) @parametrize def test_raw_response_delete(self, client: Anthropic) -> None: response = client.beta.memory_stores.with_raw_response.delete( memory_store_id="memory_store_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_store = response.parse() assert_matches_type(BetaManagedAgentsDeletedMemoryStore, memory_store, path=["response"]) @parametrize def test_streaming_response_delete(self, client: Anthropic) -> None: with client.beta.memory_stores.with_streaming_response.delete( memory_store_id="memory_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_store = response.parse() assert_matches_type(BetaManagedAgentsDeletedMemoryStore, memory_store, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_delete(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_store_id` but received ''"): client.beta.memory_stores.with_raw_response.delete( memory_store_id="", ) @parametrize def test_method_archive(self, client: Anthropic) -> None: memory_store = client.beta.memory_stores.archive( memory_store_id="memory_store_id", ) assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) @parametrize def test_method_archive_with_all_params(self, client: Anthropic) -> None: memory_store = client.beta.memory_stores.archive( memory_store_id="memory_store_id", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) @parametrize def test_raw_response_archive(self, client: Anthropic) -> None: response = client.beta.memory_stores.with_raw_response.archive( memory_store_id="memory_store_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_store = response.parse() assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) @parametrize def test_streaming_response_archive(self, client: Anthropic) -> None: with client.beta.memory_stores.with_streaming_response.archive( memory_store_id="memory_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_store = response.parse() assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_archive(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_store_id` but received ''"): client.beta.memory_stores.with_raw_response.archive( memory_store_id="", ) class TestAsyncMemoryStores: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize async def test_method_create(self, async_client: AsyncAnthropic) -> None: memory_store = await async_client.beta.memory_stores.create( name="x", ) assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) @parametrize async def test_method_create_with_all_params(self, async_client: AsyncAnthropic) -> None: memory_store = await async_client.beta.memory_stores.create( name="x", description="description", metadata={"foo": "string"}, betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) @parametrize async def test_raw_response_create(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.memory_stores.with_raw_response.create( name="x", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_store = response.parse() assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) @parametrize async def test_streaming_response_create(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.memory_stores.with_streaming_response.create( name="x", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_store = await response.parse() assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_retrieve(self, async_client: AsyncAnthropic) -> None: memory_store = await async_client.beta.memory_stores.retrieve( memory_store_id="memory_store_id", ) assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) @parametrize async def test_method_retrieve_with_all_params(self, async_client: AsyncAnthropic) -> None: memory_store = await async_client.beta.memory_stores.retrieve( memory_store_id="memory_store_id", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.memory_stores.with_raw_response.retrieve( memory_store_id="memory_store_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_store = response.parse() assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.memory_stores.with_streaming_response.retrieve( memory_store_id="memory_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_store = await response.parse() assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_retrieve(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_store_id` but received ''"): await async_client.beta.memory_stores.with_raw_response.retrieve( memory_store_id="", ) @parametrize async def test_method_update(self, async_client: AsyncAnthropic) -> None: memory_store = await async_client.beta.memory_stores.update( memory_store_id="memory_store_id", ) assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) @parametrize async def test_method_update_with_all_params(self, async_client: AsyncAnthropic) -> None: memory_store = await async_client.beta.memory_stores.update( memory_store_id="memory_store_id", description="description", metadata={"foo": "string"}, name="x", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) @parametrize async def test_raw_response_update(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.memory_stores.with_raw_response.update( memory_store_id="memory_store_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_store = response.parse() assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) @parametrize async def test_streaming_response_update(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.memory_stores.with_streaming_response.update( memory_store_id="memory_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_store = await response.parse() assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_update(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_store_id` but received ''"): await async_client.beta.memory_stores.with_raw_response.update( memory_store_id="", ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list(self, async_client: AsyncAnthropic) -> None: memory_store = await async_client.beta.memory_stores.list() assert_matches_type(AsyncPageCursor[BetaManagedAgentsMemoryStore], memory_store, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncAnthropic) -> None: memory_store = await async_client.beta.memory_stores.list( created_at_gte=parse_datetime("2019-12-27T18:11:19.117Z"), created_at_lte=parse_datetime("2019-12-27T18:11:19.117Z"), include_archived=True, limit=0, page="page", betas=["message-batches-2024-09-24"], ) assert_matches_type(AsyncPageCursor[BetaManagedAgentsMemoryStore], memory_store, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_raw_response_list(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.memory_stores.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_store = response.parse() assert_matches_type(AsyncPageCursor[BetaManagedAgentsMemoryStore], memory_store, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_streaming_response_list(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.memory_stores.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_store = await response.parse() assert_matches_type(AsyncPageCursor[BetaManagedAgentsMemoryStore], memory_store, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_delete(self, async_client: AsyncAnthropic) -> None: memory_store = await async_client.beta.memory_stores.delete( memory_store_id="memory_store_id", ) assert_matches_type(BetaManagedAgentsDeletedMemoryStore, memory_store, path=["response"]) @parametrize async def test_method_delete_with_all_params(self, async_client: AsyncAnthropic) -> None: memory_store = await async_client.beta.memory_stores.delete( memory_store_id="memory_store_id", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsDeletedMemoryStore, memory_store, path=["response"]) @parametrize async def test_raw_response_delete(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.memory_stores.with_raw_response.delete( memory_store_id="memory_store_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_store = response.parse() assert_matches_type(BetaManagedAgentsDeletedMemoryStore, memory_store, path=["response"]) @parametrize async def test_streaming_response_delete(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.memory_stores.with_streaming_response.delete( memory_store_id="memory_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_store = await response.parse() assert_matches_type(BetaManagedAgentsDeletedMemoryStore, memory_store, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_delete(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_store_id` but received ''"): await async_client.beta.memory_stores.with_raw_response.delete( memory_store_id="", ) @parametrize async def test_method_archive(self, async_client: AsyncAnthropic) -> None: memory_store = await async_client.beta.memory_stores.archive( memory_store_id="memory_store_id", ) assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) @parametrize async def test_method_archive_with_all_params(self, async_client: AsyncAnthropic) -> None: memory_store = await async_client.beta.memory_stores.archive( memory_store_id="memory_store_id", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) @parametrize async def test_raw_response_archive(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.memory_stores.with_raw_response.archive( memory_store_id="memory_store_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_store = response.parse() assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) @parametrize async def test_streaming_response_archive(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.memory_stores.with_streaming_response.archive( memory_store_id="memory_store_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" memory_store = await response.parse() assert_matches_type(BetaManagedAgentsMemoryStore, memory_store, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_archive(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `memory_store_id` but received ''"): await async_client.beta.memory_stores.with_raw_response.archive( memory_store_id="", ) anthropic-sdk-python-0.120.2/tests/api_resources/beta/test_messages.py000066400000000000000000001166731523216435200261220ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os from typing import Any, cast import pytest import pydantic from anthropic import Anthropic, AsyncAnthropic from tests.utils import assert_matches_type from anthropic.types.beta import ( BetaMessage, BetaMessageTokensCount, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestMessages: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create_overload_1(self, client: Anthropic) -> None: message = client.beta.messages.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ) assert_matches_type(BetaMessage, message, path=["response"]) @parametrize def test_method_create_with_all_params_overload_1(self, client: Anthropic) -> None: message = client.beta.messages.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", cache_control={ "type": "ephemeral", "ttl": "5m", }, container={ "id": "id", "skills": [ { "skill_id": "pdf", "type": "anthropic", "version": "latest", } ], }, context_management={ "edits": [ { "type": "clear_tool_uses_20250919", "clear_at_least": { "type": "input_tokens", "value": 0, }, "clear_tool_inputs": True, "exclude_tools": ["string"], "keep": { "type": "tool_uses", "value": 0, }, "trigger": { "type": "input_tokens", "value": 1, }, } ] }, diagnostics={"previous_message_id": "previous_message_id"}, fallback_credit_token="x", fallbacks="default", inference_geo="inference_geo", mcp_servers=[ { "name": "name", "type": "url", "url": "url", "authorization_token": "authorization_token", "tool_configuration": { "allowed_tools": ["string"], "enabled": True, }, } ], metadata={"user_id": "13803d75-b4b5-4c3e-b2a2-6f21399b021b"}, output_config={ "effort": "low", "format": { "schema": {"foo": "bar"}, "type": "json_schema", }, "task_budget": { "total": 1024, "type": "tokens", "remaining": 0, }, }, service_tier="auto", speed="standard", stop_sequences=["string"], stream=False, system=[ { "text": "Today's date is 2024-06-01.", "type": "text", "cache_control": { "type": "ephemeral", "ttl": "5m", }, "citations": [ { "cited_text": "The grass is green. The sky is blue.", "document_index": 0, "document_title": "x", "end_char_index": 0, "start_char_index": 0, "type": "char_location", } ], } ], temperature=1, thinking={ "type": "adaptive", "display": "summarized", }, tool_choice={ "type": "auto", "disable_parallel_tool_use": True, }, tools=[ { "input_schema": { "type": "object", "properties": { "location": "bar", "unit": "bar", }, "required": ["location"], }, "name": "name", "allowed_callers": ["direct"], "cache_control": { "type": "ephemeral", "ttl": "5m", }, "defer_loading": True, "description": "Get the current weather in a given location", "eager_input_streaming": True, "input_examples": [{"foo": "bar"}], "strict": True, "type": "custom", } ], top_k=5, top_p=0.7, betas=["message-batches-2024-09-24"], user_profile_id="anthropic-user-profile-id", ) assert_matches_type(BetaMessage, message, path=["response"]) @parametrize def test_raw_response_create_overload_1(self, client: Anthropic) -> None: response = client.beta.messages.with_raw_response.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() assert_matches_type(BetaMessage, message, path=["response"]) @parametrize def test_streaming_response_create_overload_1(self, client: Anthropic) -> None: with client.beta.messages.with_streaming_response.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() assert_matches_type(BetaMessage, message, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_create_overload_2(self, client: Anthropic) -> None: message_stream = client.beta.messages.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", stream=True, ) message_stream.response.close() @parametrize def test_method_create_with_all_params_overload_2(self, client: Anthropic) -> None: message_stream = client.beta.messages.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", stream=True, cache_control={ "type": "ephemeral", "ttl": "5m", }, container={ "id": "id", "skills": [ { "skill_id": "pdf", "type": "anthropic", "version": "latest", } ], }, context_management={ "edits": [ { "type": "clear_tool_uses_20250919", "clear_at_least": { "type": "input_tokens", "value": 0, }, "clear_tool_inputs": True, "exclude_tools": ["string"], "keep": { "type": "tool_uses", "value": 0, }, "trigger": { "type": "input_tokens", "value": 1, }, } ] }, diagnostics={"previous_message_id": "previous_message_id"}, fallback_credit_token="x", fallbacks="default", inference_geo="inference_geo", mcp_servers=[ { "name": "name", "type": "url", "url": "url", "authorization_token": "authorization_token", "tool_configuration": { "allowed_tools": ["string"], "enabled": True, }, } ], metadata={"user_id": "13803d75-b4b5-4c3e-b2a2-6f21399b021b"}, output_config={ "effort": "low", "format": { "schema": {"foo": "bar"}, "type": "json_schema", }, "task_budget": { "total": 1024, "type": "tokens", "remaining": 0, }, }, service_tier="auto", speed="standard", stop_sequences=["string"], system=[ { "text": "Today's date is 2024-06-01.", "type": "text", "cache_control": { "type": "ephemeral", "ttl": "5m", }, "citations": [ { "cited_text": "The grass is green. The sky is blue.", "document_index": 0, "document_title": "x", "end_char_index": 0, "start_char_index": 0, "type": "char_location", } ], } ], temperature=1, thinking={ "type": "adaptive", "display": "summarized", }, tool_choice={ "type": "auto", "disable_parallel_tool_use": True, }, tools=[ { "input_schema": { "type": "object", "properties": { "location": "bar", "unit": "bar", }, "required": ["location"], }, "name": "name", "allowed_callers": ["direct"], "cache_control": { "type": "ephemeral", "ttl": "5m", }, "defer_loading": True, "description": "Get the current weather in a given location", "eager_input_streaming": True, "input_examples": [{"foo": "bar"}], "strict": True, "type": "custom", } ], top_k=5, top_p=0.7, betas=["message-batches-2024-09-24"], user_profile_id="anthropic-user-profile-id", ) message_stream.response.close() @parametrize def test_raw_response_create_overload_2(self, client: Anthropic) -> None: response = client.beta.messages.with_raw_response.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", stream=True, ) assert response.http_request.headers.get("X-Stainless-Lang") == "python" stream = response.parse() stream.close() @parametrize def test_streaming_response_create_overload_2(self, client: Anthropic) -> None: with client.beta.messages.with_streaming_response.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", stream=True, ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" stream = response.parse() stream.close() assert cast(Any, response.is_closed) is True @parametrize def test_method_count_tokens(self, client: Anthropic) -> None: message = client.beta.messages.count_tokens( messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ) assert_matches_type(BetaMessageTokensCount, message, path=["response"]) @parametrize def test_method_count_tokens_with_all_params(self, client: Anthropic) -> None: message = client.beta.messages.count_tokens( messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", cache_control={ "type": "ephemeral", "ttl": "5m", }, context_management={ "edits": [ { "type": "clear_tool_uses_20250919", "clear_at_least": { "type": "input_tokens", "value": 0, }, "clear_tool_inputs": True, "exclude_tools": ["string"], "keep": { "type": "tool_uses", "value": 0, }, "trigger": { "type": "input_tokens", "value": 1, }, } ] }, mcp_servers=[ { "name": "name", "type": "url", "url": "url", "authorization_token": "authorization_token", "tool_configuration": { "allowed_tools": ["string"], "enabled": True, }, } ], output_config={ "effort": "low", "format": { "schema": {"foo": "bar"}, "type": "json_schema", }, "task_budget": { "total": 1024, "type": "tokens", "remaining": 0, }, }, speed="fast", system=[ { "text": "Today's date is 2024-06-01.", "type": "text", "cache_control": { "type": "ephemeral", "ttl": "5m", }, "citations": [ { "cited_text": "The grass is green. The sky is blue.", "document_index": 0, "document_title": "x", "end_char_index": 0, "start_char_index": 0, "type": "char_location", } ], } ], thinking={ "type": "adaptive", "display": "summarized", }, tool_choice={ "type": "auto", "disable_parallel_tool_use": True, }, tools=[ { "input_schema": { "type": "object", "properties": { "location": "bar", "unit": "bar", }, "required": ["location"], }, "name": "name", "allowed_callers": ["direct"], "cache_control": { "type": "ephemeral", "ttl": "5m", }, "defer_loading": True, "description": "Get the current weather in a given location", "eager_input_streaming": True, "input_examples": [{"foo": "bar"}], "strict": True, "type": "custom", } ], betas=["string"], ) assert_matches_type(BetaMessageTokensCount, message, path=["response"]) @parametrize def test_raw_response_count_tokens(self, client: Anthropic) -> None: response = client.beta.messages.with_raw_response.count_tokens( messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() assert_matches_type(BetaMessageTokensCount, message, path=["response"]) @parametrize def test_streaming_response_count_tokens(self, client: Anthropic) -> None: with client.beta.messages.with_streaming_response.count_tokens( messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() assert_matches_type(BetaMessageTokensCount, message, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_pydantic_error_in_create(self, client: Anthropic) -> None: class MyModel(pydantic.BaseModel): name: str age: int with pytest.raises(TypeError) as exc_info: client.beta.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Test"}], model="claude-sonnet-4-5-20250929", output_format=MyModel, # type: ignore ) error_message = str(exc_info.value) assert "parse()" in error_message class TestAsyncMessages: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize async def test_method_create_overload_1(self, async_client: AsyncAnthropic) -> None: message = await async_client.beta.messages.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ) assert_matches_type(BetaMessage, message, path=["response"]) @parametrize async def test_method_create_with_all_params_overload_1(self, async_client: AsyncAnthropic) -> None: message = await async_client.beta.messages.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", cache_control={ "type": "ephemeral", "ttl": "5m", }, container={ "id": "id", "skills": [ { "skill_id": "pdf", "type": "anthropic", "version": "latest", } ], }, context_management={ "edits": [ { "type": "clear_tool_uses_20250919", "clear_at_least": { "type": "input_tokens", "value": 0, }, "clear_tool_inputs": True, "exclude_tools": ["string"], "keep": { "type": "tool_uses", "value": 0, }, "trigger": { "type": "input_tokens", "value": 1, }, } ] }, diagnostics={"previous_message_id": "previous_message_id"}, fallback_credit_token="x", fallbacks="default", inference_geo="inference_geo", mcp_servers=[ { "name": "name", "type": "url", "url": "url", "authorization_token": "authorization_token", "tool_configuration": { "allowed_tools": ["string"], "enabled": True, }, } ], metadata={"user_id": "13803d75-b4b5-4c3e-b2a2-6f21399b021b"}, output_config={ "effort": "low", "format": { "schema": {"foo": "bar"}, "type": "json_schema", }, "task_budget": { "total": 1024, "type": "tokens", "remaining": 0, }, }, service_tier="auto", speed="standard", stop_sequences=["string"], stream=False, system=[ { "text": "Today's date is 2024-06-01.", "type": "text", "cache_control": { "type": "ephemeral", "ttl": "5m", }, "citations": [ { "cited_text": "The grass is green. The sky is blue.", "document_index": 0, "document_title": "x", "end_char_index": 0, "start_char_index": 0, "type": "char_location", } ], } ], temperature=1, thinking={ "type": "adaptive", "display": "summarized", }, tool_choice={ "type": "auto", "disable_parallel_tool_use": True, }, tools=[ { "input_schema": { "type": "object", "properties": { "location": "bar", "unit": "bar", }, "required": ["location"], }, "name": "name", "allowed_callers": ["direct"], "cache_control": { "type": "ephemeral", "ttl": "5m", }, "defer_loading": True, "description": "Get the current weather in a given location", "eager_input_streaming": True, "input_examples": [{"foo": "bar"}], "strict": True, "type": "custom", } ], top_k=5, top_p=0.7, betas=["message-batches-2024-09-24"], user_profile_id="anthropic-user-profile-id", ) assert_matches_type(BetaMessage, message, path=["response"]) @parametrize async def test_raw_response_create_overload_1(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.messages.with_raw_response.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() assert_matches_type(BetaMessage, message, path=["response"]) @parametrize async def test_streaming_response_create_overload_1(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.messages.with_streaming_response.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = await response.parse() assert_matches_type(BetaMessage, message, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_create_overload_2(self, async_client: AsyncAnthropic) -> None: message_stream = await async_client.beta.messages.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", stream=True, ) await message_stream.response.aclose() @parametrize async def test_method_create_with_all_params_overload_2(self, async_client: AsyncAnthropic) -> None: message_stream = await async_client.beta.messages.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", stream=True, cache_control={ "type": "ephemeral", "ttl": "5m", }, container={ "id": "id", "skills": [ { "skill_id": "pdf", "type": "anthropic", "version": "latest", } ], }, context_management={ "edits": [ { "type": "clear_tool_uses_20250919", "clear_at_least": { "type": "input_tokens", "value": 0, }, "clear_tool_inputs": True, "exclude_tools": ["string"], "keep": { "type": "tool_uses", "value": 0, }, "trigger": { "type": "input_tokens", "value": 1, }, } ] }, diagnostics={"previous_message_id": "previous_message_id"}, fallback_credit_token="x", fallbacks="default", inference_geo="inference_geo", mcp_servers=[ { "name": "name", "type": "url", "url": "url", "authorization_token": "authorization_token", "tool_configuration": { "allowed_tools": ["string"], "enabled": True, }, } ], metadata={"user_id": "13803d75-b4b5-4c3e-b2a2-6f21399b021b"}, output_config={ "effort": "low", "format": { "schema": {"foo": "bar"}, "type": "json_schema", }, "task_budget": { "total": 1024, "type": "tokens", "remaining": 0, }, }, service_tier="auto", speed="standard", stop_sequences=["string"], system=[ { "text": "Today's date is 2024-06-01.", "type": "text", "cache_control": { "type": "ephemeral", "ttl": "5m", }, "citations": [ { "cited_text": "The grass is green. The sky is blue.", "document_index": 0, "document_title": "x", "end_char_index": 0, "start_char_index": 0, "type": "char_location", } ], } ], temperature=1, thinking={ "type": "adaptive", "display": "summarized", }, tool_choice={ "type": "auto", "disable_parallel_tool_use": True, }, tools=[ { "input_schema": { "type": "object", "properties": { "location": "bar", "unit": "bar", }, "required": ["location"], }, "name": "name", "allowed_callers": ["direct"], "cache_control": { "type": "ephemeral", "ttl": "5m", }, "defer_loading": True, "description": "Get the current weather in a given location", "eager_input_streaming": True, "input_examples": [{"foo": "bar"}], "strict": True, "type": "custom", } ], top_k=5, top_p=0.7, betas=["message-batches-2024-09-24"], user_profile_id="anthropic-user-profile-id", ) await message_stream.response.aclose() @parametrize async def test_raw_response_create_overload_2(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.messages.with_raw_response.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", stream=True, ) assert response.http_request.headers.get("X-Stainless-Lang") == "python" stream = response.parse() await stream.close() @parametrize async def test_streaming_response_create_overload_2(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.messages.with_streaming_response.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", stream=True, ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" stream = await response.parse() await stream.close() assert cast(Any, response.is_closed) is True @parametrize async def test_method_count_tokens(self, async_client: AsyncAnthropic) -> None: message = await async_client.beta.messages.count_tokens( messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ) assert_matches_type(BetaMessageTokensCount, message, path=["response"]) @parametrize async def test_method_count_tokens_with_all_params(self, async_client: AsyncAnthropic) -> None: message = await async_client.beta.messages.count_tokens( messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", cache_control={ "type": "ephemeral", "ttl": "5m", }, context_management={ "edits": [ { "type": "clear_tool_uses_20250919", "clear_at_least": { "type": "input_tokens", "value": 0, }, "clear_tool_inputs": True, "exclude_tools": ["string"], "keep": { "type": "tool_uses", "value": 0, }, "trigger": { "type": "input_tokens", "value": 1, }, } ] }, mcp_servers=[ { "name": "name", "type": "url", "url": "url", "authorization_token": "authorization_token", "tool_configuration": { "allowed_tools": ["string"], "enabled": True, }, } ], output_config={ "effort": "low", "format": { "schema": {"foo": "bar"}, "type": "json_schema", }, "task_budget": { "total": 1024, "type": "tokens", "remaining": 0, }, }, speed="fast", system=[ { "text": "Today's date is 2024-06-01.", "type": "text", "cache_control": { "type": "ephemeral", "ttl": "5m", }, "citations": [ { "cited_text": "The grass is green. The sky is blue.", "document_index": 0, "document_title": "x", "end_char_index": 0, "start_char_index": 0, "type": "char_location", } ], } ], thinking={ "type": "adaptive", "display": "summarized", }, tool_choice={ "type": "auto", "disable_parallel_tool_use": True, }, tools=[ { "input_schema": { "type": "object", "properties": { "location": "bar", "unit": "bar", }, "required": ["location"], }, "name": "name", "allowed_callers": ["direct"], "cache_control": { "type": "ephemeral", "ttl": "5m", }, "defer_loading": True, "description": "Get the current weather in a given location", "eager_input_streaming": True, "input_examples": [{"foo": "bar"}], "strict": True, "type": "custom", } ], betas=["string"], user_profile_id="anthropic-user-profile-id", ) assert_matches_type(BetaMessageTokensCount, message, path=["response"]) @parametrize async def test_raw_response_count_tokens(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.messages.with_raw_response.count_tokens( messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() assert_matches_type(BetaMessageTokensCount, message, path=["response"]) @parametrize async def test_streaming_response_count_tokens(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.messages.with_streaming_response.count_tokens( messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = await response.parse() assert_matches_type(BetaMessageTokensCount, message, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_pydantic_error_in_create(self, async_client: AsyncAnthropic) -> None: class MyModel(pydantic.BaseModel): name: str age: int with pytest.raises(TypeError) as exc_info: await async_client.beta.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Test"}], model="claude-sonnet-4-5-20250929", output_format=MyModel, # type: ignore ) error_message = str(exc_info.value) assert "parse()" in error_message anthropic-sdk-python-0.120.2/tests/api_resources/beta/test_models.py000066400000000000000000000162161523216435200255660ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os from typing import Any, cast import pytest from anthropic import Anthropic, AsyncAnthropic from tests.utils import assert_matches_type from anthropic.pagination import SyncPage, AsyncPage from anthropic.types.beta import BetaModelInfo base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestModels: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_retrieve(self, client: Anthropic) -> None: model = client.beta.models.retrieve( model_id="model_id", ) assert_matches_type(BetaModelInfo, model, path=["response"]) @parametrize def test_method_retrieve_with_all_params(self, client: Anthropic) -> None: model = client.beta.models.retrieve( model_id="model_id", betas=["string"], ) assert_matches_type(BetaModelInfo, model, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: Anthropic) -> None: response = client.beta.models.with_raw_response.retrieve( model_id="model_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = response.parse() assert_matches_type(BetaModelInfo, model, path=["response"]) @parametrize def test_streaming_response_retrieve(self, client: Anthropic) -> None: with client.beta.models.with_streaming_response.retrieve( model_id="model_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = response.parse() assert_matches_type(BetaModelInfo, model, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_retrieve(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `model_id` but received ''"): client.beta.models.with_raw_response.retrieve( model_id="", ) @parametrize def test_method_list(self, client: Anthropic) -> None: model = client.beta.models.list() assert_matches_type(SyncPage[BetaModelInfo], model, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: Anthropic) -> None: model = client.beta.models.list( after_id="after_id", before_id="before_id", limit=1, betas=["string"], ) assert_matches_type(SyncPage[BetaModelInfo], model, path=["response"]) @parametrize def test_raw_response_list(self, client: Anthropic) -> None: response = client.beta.models.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = response.parse() assert_matches_type(SyncPage[BetaModelInfo], model, path=["response"]) @parametrize def test_streaming_response_list(self, client: Anthropic) -> None: with client.beta.models.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = response.parse() assert_matches_type(SyncPage[BetaModelInfo], model, path=["response"]) assert cast(Any, response.is_closed) is True class TestAsyncModels: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize async def test_method_retrieve(self, async_client: AsyncAnthropic) -> None: model = await async_client.beta.models.retrieve( model_id="model_id", ) assert_matches_type(BetaModelInfo, model, path=["response"]) @parametrize async def test_method_retrieve_with_all_params(self, async_client: AsyncAnthropic) -> None: model = await async_client.beta.models.retrieve( model_id="model_id", betas=["string"], ) assert_matches_type(BetaModelInfo, model, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.models.with_raw_response.retrieve( model_id="model_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = response.parse() assert_matches_type(BetaModelInfo, model, path=["response"]) @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.models.with_streaming_response.retrieve( model_id="model_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = await response.parse() assert_matches_type(BetaModelInfo, model, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_retrieve(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `model_id` but received ''"): await async_client.beta.models.with_raw_response.retrieve( model_id="", ) @parametrize async def test_method_list(self, async_client: AsyncAnthropic) -> None: model = await async_client.beta.models.list() assert_matches_type(AsyncPage[BetaModelInfo], model, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncAnthropic) -> None: model = await async_client.beta.models.list( after_id="after_id", before_id="before_id", limit=1, betas=["string"], ) assert_matches_type(AsyncPage[BetaModelInfo], model, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.models.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = response.parse() assert_matches_type(AsyncPage[BetaModelInfo], model, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.models.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = await response.parse() assert_matches_type(AsyncPage[BetaModelInfo], model, path=["response"]) assert cast(Any, response.is_closed) is True anthropic-sdk-python-0.120.2/tests/api_resources/beta/test_output_format_conversion.py000066400000000000000000000607041523216435200314610ustar00rootroot00000000000000"""Tests for output_format to output_config.format conversion and deprecation.""" import json import warnings import httpx import pytest from respx import MockRouter from pydantic import BaseModel from anthropic import Anthropic, AnthropicError, AsyncAnthropic, _compat class TestOutputFormatConversion: """Test that output_format is properly converted to output_config.format.""" def test_create_converts_output_format_to_output_config(self, client: Anthropic, respx_mock: MockRouter) -> None: """Verify .create() converts output_format to output_config.format in request body.""" respx_mock.post("/v1/messages?beta=true").mock( return_value=httpx.Response( 200, json={ "id": "msg_123", "type": "message", "role": "assistant", "model": "claude-sonnet-4-5", "content": [{"text": '{"result": "test"}', "type": "text"}], "stop_reason": "end_turn", "usage": {"input_tokens": 10, "output_tokens": 20}, }, ) ) with warnings.catch_warnings(record=True): warnings.simplefilter("always") client.beta.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Test"}], model="claude-sonnet-4-5", output_format={"type": "json_schema", "schema": {"type": "object"}}, ) request = respx_mock.calls.last.request body = json.loads(request.content) # Should have output_config with format assert "output_config" in body assert "format" in body["output_config"] assert body["output_config"]["format"]["type"] == "json_schema" assert body["output_config"]["format"]["schema"]["type"] == "object" # Should NOT have output_format in request assert "output_format" not in body @pytest.mark.skipif(_compat.PYDANTIC_V1, reason="parse with Pydantic models requires Pydantic v2") def test_parse_converts_pydantic_to_output_config(self, client: Anthropic, respx_mock: MockRouter) -> None: """Verify .parse() converts Pydantic models to output_config.format.""" class User(BaseModel): name: str age: int respx_mock.post("/v1/messages?beta=true").mock( return_value=httpx.Response( 200, json={ "id": "msg_123", "type": "message", "role": "assistant", "model": "claude-sonnet-4-5", "content": [{"text": '{"name": "John", "age": 30}', "type": "text"}], "stop_reason": "end_turn", "usage": {"input_tokens": 10, "output_tokens": 20}, }, ) ) with warnings.catch_warnings(record=True): warnings.simplefilter("always") client.beta.messages.parse( max_tokens=1024, messages=[{"role": "user", "content": "Test"}], model="claude-sonnet-4-5", output_format=User, ) request = respx_mock.calls.last.request body = json.loads(request.content) # Should have output_config with format containing the User schema assert "output_config" in body assert "format" in body["output_config"] assert body["output_config"]["format"]["type"] == "json_schema" assert "properties" in body["output_config"]["format"]["schema"] assert "name" in body["output_config"]["format"]["schema"]["properties"] assert "age" in body["output_config"]["format"]["schema"]["properties"] # Should NOT have output_format in request assert "output_format" not in body def test_stream_converts_output_format_to_output_config(self, client: Anthropic, respx_mock: MockRouter) -> None: """Verify .stream() converts output_format to output_config.format.""" respx_mock.post("/v1/messages?beta=true").mock( return_value=httpx.Response( 200, json={ "id": "msg_123", "type": "message", "role": "assistant", "model": "claude-sonnet-4-5", "content": [{"text": "test", "type": "text"}], "stop_reason": "end_turn", "usage": {"input_tokens": 10, "output_tokens": 20}, }, ) ) with warnings.catch_warnings(record=True): warnings.simplefilter("always") with client.beta.messages.stream( max_tokens=1024, messages=[{"role": "user", "content": "Test"}], model="claude-sonnet-4-5", output_format={"type": "json_schema", "schema": {"type": "string"}}, ) as stream: # type: ignore pass request = respx_mock.calls.last.request body = json.loads(request.content) assert "output_config" in body assert "format" in body["output_config"] assert body["output_config"]["format"]["type"] == "json_schema" assert "output_format" not in body def test_count_tokens_converts_output_format_to_output_config( self, client: Anthropic, respx_mock: MockRouter ) -> None: """Verify .count_tokens() converts output_format to output_config.format.""" respx_mock.post("/v1/messages/count_tokens?beta=true").mock( return_value=httpx.Response(200, json={"input_tokens": 10}) ) with warnings.catch_warnings(record=True): warnings.simplefilter("always") client.beta.messages.count_tokens( messages=[{"role": "user", "content": "Test"}], model="claude-sonnet-4-5", output_format={"type": "json_schema", "schema": {"type": "array"}}, ) request = respx_mock.calls.last.request body = json.loads(request.content) assert "output_config" in body assert "format" in body["output_config"] assert body["output_config"]["format"]["type"] == "json_schema" assert "output_format" not in body class TestOutputFormatDeprecation: """Test that output_format parameter emits deprecation warnings.""" def test_create_emits_deprecation_warning(self, client: Anthropic, respx_mock: MockRouter) -> None: """Verify .create() emits DeprecationWarning when output_format is used.""" respx_mock.post("/v1/messages?beta=true").mock( return_value=httpx.Response( 200, json={ "id": "msg_123", "type": "message", "role": "assistant", "model": "claude-sonnet-4-5", "content": [{"text": "test", "type": "text"}], "stop_reason": "end_turn", "usage": {"input_tokens": 10, "output_tokens": 20}, }, ) ) with pytest.warns(DeprecationWarning, match="output_format.*deprecated.*output_config.format"): client.beta.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Test"}], model="claude-sonnet-4-5", output_format={"type": "json_schema", "schema": {"type": "object"}}, ) @pytest.mark.skipif(_compat.PYDANTIC_V1, reason="parse with Pydantic models requires Pydantic v2") def test_parse_emits_deprecation_warning(self, client: Anthropic, respx_mock: MockRouter) -> None: """Verify .parse() emits DeprecationWarning when output_format is used.""" class SimpleModel(BaseModel): value: str respx_mock.post("/v1/messages?beta=true").mock( return_value=httpx.Response( 200, json={ "id": "msg_123", "type": "message", "role": "assistant", "model": "claude-sonnet-4-5", "content": [{"text": '{"value": "test"}', "type": "text"}], "stop_reason": "end_turn", "usage": {"input_tokens": 10, "output_tokens": 20}, }, ) ) with pytest.warns(DeprecationWarning, match="output_format.*deprecated.*output_config.format"): client.beta.messages.parse( max_tokens=1024, messages=[{"role": "user", "content": "Test"}], model="claude-sonnet-4-5", output_format=SimpleModel, ) def test_stream_emits_deprecation_warning(self, client: Anthropic, respx_mock: MockRouter) -> None: """Verify .stream() emits DeprecationWarning when output_format is used.""" respx_mock.post("/v1/messages?beta=true").mock( return_value=httpx.Response( 200, json={ "id": "msg_123", "type": "message", "role": "assistant", "model": "claude-sonnet-4-5", "content": [{"text": "test", "type": "text"}], "stop_reason": "end_turn", "usage": {"input_tokens": 10, "output_tokens": 20}, }, ) ) with pytest.warns(DeprecationWarning, match="output_format.*deprecated.*output_config.format"): with client.beta.messages.stream( max_tokens=1024, messages=[{"role": "user", "content": "Test"}], model="claude-sonnet-4-5", output_format={"type": "json_schema", "schema": {"type": "string"}}, ) as stream: # type: ignore pass def test_count_tokens_emits_deprecation_warning(self, client: Anthropic, respx_mock: MockRouter) -> None: """Verify .count_tokens() emits DeprecationWarning when output_format is used.""" respx_mock.post("/v1/messages/count_tokens?beta=true").mock( return_value=httpx.Response(200, json={"input_tokens": 10}) ) with pytest.warns(DeprecationWarning, match="output_format.*deprecated.*output_config.format"): client.beta.messages.count_tokens( messages=[{"role": "user", "content": "Test"}], model="claude-sonnet-4-5", output_format={"type": "json_schema", "schema": {"type": "array"}}, ) def test_no_warning_when_output_format_not_provided(self, client: Anthropic, respx_mock: MockRouter) -> None: """Verify no deprecation warning when output_format is not used.""" respx_mock.post("/v1/messages?beta=true").mock( return_value=httpx.Response( 200, json={ "id": "msg_123", "type": "message", "role": "assistant", "model": "claude-sonnet-4-5", "content": [{"text": "test", "type": "text"}], "stop_reason": "end_turn", "usage": {"input_tokens": 10, "output_tokens": 20}, }, ) ) with warnings.catch_warnings(): warnings.simplefilter("error", DeprecationWarning) # Should not raise any warnings client.beta.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Test"}], model="claude-sonnet-4-5", ) def test_no_warning_when_using_output_config(self, client: Anthropic, respx_mock: MockRouter) -> None: """Verify no deprecation warning when using output_config.format directly.""" respx_mock.post("/v1/messages?beta=true").mock( return_value=httpx.Response( 200, json={ "id": "msg_123", "type": "message", "role": "assistant", "model": "claude-sonnet-4-5", "content": [{"text": "test", "type": "text"}], "stop_reason": "end_turn", "usage": {"input_tokens": 10, "output_tokens": 20}, }, ) ) with warnings.catch_warnings(): warnings.simplefilter("error", DeprecationWarning) # Should not raise any warnings client.beta.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Test"}], model="claude-sonnet-4-5", output_config={"format": {"type": "json_schema", "schema": {"type": "object"}}}, ) class TestOutputConfigConflict: """Test that providing both output_format and output_config.format raises an error.""" def test_create_rejects_both_output_format_and_config(self, client: Anthropic) -> None: """Verify .create() raises error when both output_format and output_config.format are provided.""" with pytest.raises(AnthropicError, match="Both output_format and output_config.format were provided"): client.beta.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Test"}], model="claude-sonnet-4-5", output_format={"type": "json_schema", "schema": {"type": "object"}}, output_config={"format": {"type": "json_schema", "schema": {"type": "string"}}}, ) def test_parse_rejects_both_output_format_and_config(self, client: Anthropic) -> None: """Verify .parse() raises error when both output_format and output_config.format are provided.""" class TestModel(BaseModel): value: str with pytest.raises(AnthropicError, match="Both output_format and output_config.format were provided"): client.beta.messages.parse( max_tokens=1024, messages=[{"role": "user", "content": "Test"}], model="claude-sonnet-4-5", output_format=TestModel, output_config={"format": {"type": "json_schema", "schema": {"type": "string"}}}, ) def test_count_tokens_rejects_both_output_format_and_config(self, client: Anthropic) -> None: """Verify .count_tokens() raises error when both are provided.""" with pytest.raises(AnthropicError, match="Both output_format and output_config.format were provided"): client.beta.messages.count_tokens( messages=[{"role": "user", "content": "Test"}], model="claude-sonnet-4-5", output_format={"type": "json_schema", "schema": {"type": "object"}}, output_config={"format": {"type": "json_schema", "schema": {"type": "string"}}}, ) def test_allows_output_config_without_format(self, client: Anthropic, respx_mock: MockRouter) -> None: """Verify output_config without format field can be used with output_format.""" respx_mock.post("/v1/messages?beta=true").mock( return_value=httpx.Response( 200, json={ "id": "msg_123", "type": "message", "role": "assistant", "model": "claude-sonnet-4-5", "content": [{"text": "test", "type": "text"}], "stop_reason": "end_turn", "usage": {"input_tokens": 10, "output_tokens": 20}, }, ) ) with warnings.catch_warnings(record=True): warnings.simplefilter("always") # Should succeed - output_config.effort is fine with output_format client.beta.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Test"}], model="claude-sonnet-4-5", output_format={"type": "json_schema", "schema": {"type": "object"}}, output_config={"effort": "high"}, # No format field, so no conflict ) class TestStructuredOutputsBetaHeader: """Test that structured-outputs-2025-12-15 beta header is added for .parse().""" @pytest.mark.skipif(_compat.PYDANTIC_V1, reason="parse with Pydantic models requires Pydantic v2") def test_parse_adds_structured_outputs_beta_header(self, client: Anthropic, respx_mock: MockRouter) -> None: """Verify .parse() auto-adds structured-outputs-2025-12-15 beta header.""" class DataModel(BaseModel): value: int respx_mock.post("/v1/messages?beta=true").mock( return_value=httpx.Response( 200, json={ "id": "msg_123", "type": "message", "role": "assistant", "model": "claude-sonnet-4-5", "content": [{"text": '{"value": 42}', "type": "text"}], "stop_reason": "end_turn", "usage": {"input_tokens": 10, "output_tokens": 20}, }, ) ) with warnings.catch_warnings(record=True): warnings.simplefilter("always") client.beta.messages.parse( max_tokens=1024, messages=[{"role": "user", "content": "Test"}], model="claude-sonnet-4-5", output_format=DataModel, ) request = respx_mock.calls.last.request assert "anthropic-beta" in request.headers assert "structured-outputs-2025-12-15" in request.headers["anthropic-beta"] @pytest.mark.skipif(_compat.PYDANTIC_V1, reason="parse with Pydantic models requires Pydantic v2") def test_parse_preserves_existing_betas(self, client: Anthropic, respx_mock: MockRouter) -> None: """Verify .parse() preserves other beta headers when adding structured-outputs.""" class DataModel(BaseModel): value: int respx_mock.post("/v1/messages?beta=true").mock( return_value=httpx.Response( 200, json={ "id": "msg_123", "type": "message", "role": "assistant", "model": "claude-sonnet-4-5", "content": [{"text": '{"value": 42}', "type": "text"}], "stop_reason": "end_turn", "usage": {"input_tokens": 10, "output_tokens": 20}, }, ) ) with warnings.catch_warnings(record=True): warnings.simplefilter("always") client.beta.messages.parse( max_tokens=1024, messages=[{"role": "user", "content": "Test"}], model="claude-sonnet-4-5", output_format=DataModel, betas=["some-other-beta-feature"], ) request = respx_mock.calls.last.request beta_header = request.headers["anthropic-beta"] assert "structured-outputs-2025-12-15" in beta_header assert "some-other-beta-feature" in beta_header @pytest.mark.skipif(_compat.PYDANTIC_V1, reason="parse with Pydantic models requires Pydantic v2") def test_parse_does_not_duplicate_beta_header(self, client: Anthropic, respx_mock: MockRouter) -> None: """Verify .parse() doesn't duplicate structured-outputs beta if already present.""" class DataModel(BaseModel): value: int respx_mock.post("/v1/messages?beta=true").mock( return_value=httpx.Response( 200, json={ "id": "msg_123", "type": "message", "role": "assistant", "model": "claude-sonnet-4-5", "content": [{"text": '{"value": 42}', "type": "text"}], "stop_reason": "end_turn", "usage": {"input_tokens": 10, "output_tokens": 20}, }, ) ) with warnings.catch_warnings(record=True): warnings.simplefilter("always") client.beta.messages.parse( max_tokens=1024, messages=[{"role": "user", "content": "Test"}], model="claude-sonnet-4-5", output_format=DataModel, betas=["structured-outputs-2025-12-15"], ) request = respx_mock.calls.last.request beta_header = request.headers["anthropic-beta"] # Should only appear once assert beta_header.count("structured-outputs-2025-12-15") == 1 class TestAsyncOutputFormatConversion: """Test async variants of output_format conversion.""" async def test_async_create_converts_output_format( self, async_client: AsyncAnthropic, respx_mock: MockRouter ) -> None: """Verify async .create() converts output_format to output_config.format.""" respx_mock.post("/v1/messages?beta=true").mock( return_value=httpx.Response( 200, json={ "id": "msg_123", "type": "message", "role": "assistant", "model": "claude-sonnet-4-5", "content": [{"text": "test", "type": "text"}], "stop_reason": "end_turn", "usage": {"input_tokens": 10, "output_tokens": 20}, }, ) ) with warnings.catch_warnings(record=True): warnings.simplefilter("always") await async_client.beta.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Test"}], model="claude-sonnet-4-5", output_format={"type": "json_schema", "schema": {"type": "object"}}, ) request = respx_mock.calls.last.request body = json.loads(request.content) assert "output_config" in body assert "format" in body["output_config"] assert "output_format" not in body @pytest.mark.skipif(_compat.PYDANTIC_V1, reason="parse with Pydantic models requires Pydantic v2") async def test_async_parse_converts_pydantic(self, async_client: AsyncAnthropic, respx_mock: MockRouter) -> None: """Verify async .parse() converts Pydantic models to output_config.format.""" class User(BaseModel): name: str respx_mock.post("/v1/messages?beta=true").mock( return_value=httpx.Response( 200, json={ "id": "msg_123", "type": "message", "role": "assistant", "model": "claude-sonnet-4-5", "content": [{"text": '{"name": "John"}', "type": "text"}], "stop_reason": "end_turn", "usage": {"input_tokens": 10, "output_tokens": 20}, }, ) ) with warnings.catch_warnings(record=True): warnings.simplefilter("always") await async_client.beta.messages.parse( max_tokens=1024, messages=[{"role": "user", "content": "Test"}], model="claude-sonnet-4-5", output_format=User, ) request = respx_mock.calls.last.request body = json.loads(request.content) assert "output_config" in body assert "format" in body["output_config"] assert "output_format" not in body async def test_async_methods_emit_deprecation_warnings( self, async_client: AsyncAnthropic, respx_mock: MockRouter ) -> None: """Verify async methods emit DeprecationWarning.""" respx_mock.post("/v1/messages?beta=true").mock( return_value=httpx.Response( 200, json={ "id": "msg_123", "type": "message", "role": "assistant", "model": "claude-sonnet-4-5", "content": [{"text": "test", "type": "text"}], "stop_reason": "end_turn", "usage": {"input_tokens": 10, "output_tokens": 20}, }, ) ) with pytest.warns(DeprecationWarning, match="output_format.*deprecated"): await async_client.beta.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Test"}], model="claude-sonnet-4-5", output_format={"type": "json_schema", "schema": {"type": "object"}}, ) anthropic-sdk-python-0.120.2/tests/api_resources/beta/test_sessions.py000066400000000000000000000702441523216435200261520ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os from typing import Any, cast import pytest from anthropic import Anthropic, AsyncAnthropic from tests.utils import assert_matches_type from anthropic._utils import parse_datetime from anthropic.pagination import SyncBidirectionalPageCursor, AsyncBidirectionalPageCursor from anthropic.types.beta import ( BetaManagedAgentsSession, BetaManagedAgentsDeletedSession, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestSessions: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create(self, client: Anthropic) -> None: session = client.beta.sessions.create( agent="agent_011CZkYpogX7uDKUyvBTophP", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) @parametrize def test_method_create_with_all_params(self, client: Anthropic) -> None: session = client.beta.sessions.create( agent="agent_011CZkYpogX7uDKUyvBTophP", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", initial_events=[ { "content": [ { "text": "Where is my order #1234?", "type": "text", } ], "type": "user.message", } ], metadata={"foo": "string"}, resources=[ { "file_id": "file_011CNha8iCJcU1wXNR6q4V8w", "type": "file", "mount_path": "/uploads/receipt.pdf", } ], title="Order #1234 inquiry", vault_ids=["string"], betas=["string"], ) assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) @parametrize def test_raw_response_create(self, client: Anthropic) -> None: response = client.beta.sessions.with_raw_response.create( agent="agent_011CZkYpogX7uDKUyvBTophP", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) @parametrize def test_streaming_response_create(self, client: Anthropic) -> None: with client.beta.sessions.with_streaming_response.create( agent="agent_011CZkYpogX7uDKUyvBTophP", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_retrieve(self, client: Anthropic) -> None: session = client.beta.sessions.retrieve( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) @parametrize def test_method_retrieve_with_all_params(self, client: Anthropic) -> None: session = client.beta.sessions.retrieve( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", betas=["string"], ) assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: Anthropic) -> None: response = client.beta.sessions.with_raw_response.retrieve( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) @parametrize def test_streaming_response_retrieve(self, client: Anthropic) -> None: with client.beta.sessions.with_streaming_response.retrieve( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_retrieve(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): client.beta.sessions.with_raw_response.retrieve( session_id="", ) @parametrize def test_method_update(self, client: Anthropic) -> None: session = client.beta.sessions.update( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) @parametrize def test_method_update_with_all_params(self, client: Anthropic) -> None: session = client.beta.sessions.update( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", agent={ "mcp_servers": [ { "name": "example-mcp", "type": "url", "url": "https://example-server.modelcontextprotocol.io/sse", } ], "tools": [ { "type": "agent_toolset_20260401", "configs": [ { "name": "bash", "enabled": True, "permission_policy": {"type": "always_allow"}, } ], "default_config": { "enabled": True, "permission_policy": {"type": "always_allow"}, }, } ], }, metadata={"foo": "string"}, title="Order #1234 inquiry", vault_ids=["string"], betas=["string"], ) assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) @parametrize def test_raw_response_update(self, client: Anthropic) -> None: response = client.beta.sessions.with_raw_response.update( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) @parametrize def test_streaming_response_update(self, client: Anthropic) -> None: with client.beta.sessions.with_streaming_response.update( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_update(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): client.beta.sessions.with_raw_response.update( session_id="", ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list(self, client: Anthropic) -> None: session = client.beta.sessions.list() assert_matches_type(SyncBidirectionalPageCursor[BetaManagedAgentsSession], session, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list_with_all_params(self, client: Anthropic) -> None: session = client.beta.sessions.list( agent_id="agent_id", agent_version=0, created_at_gt=parse_datetime("2019-12-27T18:11:19.117Z"), created_at_gte=parse_datetime("2019-12-27T18:11:19.117Z"), created_at_lt=parse_datetime("2019-12-27T18:11:19.117Z"), created_at_lte=parse_datetime("2019-12-27T18:11:19.117Z"), deployment_id="deployment_id", include_archived=True, limit=0, memory_store_id="memory_store_id", order="asc", page="page", statuses=["rescheduling"], betas=["string"], ) assert_matches_type(SyncBidirectionalPageCursor[BetaManagedAgentsSession], session, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_raw_response_list(self, client: Anthropic) -> None: response = client.beta.sessions.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() assert_matches_type(SyncBidirectionalPageCursor[BetaManagedAgentsSession], session, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_streaming_response_list(self, client: Anthropic) -> None: with client.beta.sessions.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() assert_matches_type(SyncBidirectionalPageCursor[BetaManagedAgentsSession], session, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_delete(self, client: Anthropic) -> None: session = client.beta.sessions.delete( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert_matches_type(BetaManagedAgentsDeletedSession, session, path=["response"]) @parametrize def test_method_delete_with_all_params(self, client: Anthropic) -> None: session = client.beta.sessions.delete( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", betas=["string"], ) assert_matches_type(BetaManagedAgentsDeletedSession, session, path=["response"]) @parametrize def test_raw_response_delete(self, client: Anthropic) -> None: response = client.beta.sessions.with_raw_response.delete( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() assert_matches_type(BetaManagedAgentsDeletedSession, session, path=["response"]) @parametrize def test_streaming_response_delete(self, client: Anthropic) -> None: with client.beta.sessions.with_streaming_response.delete( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() assert_matches_type(BetaManagedAgentsDeletedSession, session, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_delete(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): client.beta.sessions.with_raw_response.delete( session_id="", ) @parametrize def test_method_archive(self, client: Anthropic) -> None: session = client.beta.sessions.archive( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) @parametrize def test_method_archive_with_all_params(self, client: Anthropic) -> None: session = client.beta.sessions.archive( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", betas=["string"], ) assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) @parametrize def test_raw_response_archive(self, client: Anthropic) -> None: response = client.beta.sessions.with_raw_response.archive( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) @parametrize def test_streaming_response_archive(self, client: Anthropic) -> None: with client.beta.sessions.with_streaming_response.archive( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_archive(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): client.beta.sessions.with_raw_response.archive( session_id="", ) class TestAsyncSessions: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize async def test_method_create(self, async_client: AsyncAnthropic) -> None: session = await async_client.beta.sessions.create( agent="agent_011CZkYpogX7uDKUyvBTophP", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) @parametrize async def test_method_create_with_all_params(self, async_client: AsyncAnthropic) -> None: session = await async_client.beta.sessions.create( agent="agent_011CZkYpogX7uDKUyvBTophP", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", initial_events=[ { "content": [ { "text": "Where is my order #1234?", "type": "text", } ], "type": "user.message", } ], metadata={"foo": "string"}, resources=[ { "file_id": "file_011CNha8iCJcU1wXNR6q4V8w", "type": "file", "mount_path": "/uploads/receipt.pdf", } ], title="Order #1234 inquiry", vault_ids=["string"], betas=["string"], ) assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) @parametrize async def test_raw_response_create(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.sessions.with_raw_response.create( agent="agent_011CZkYpogX7uDKUyvBTophP", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) @parametrize async def test_streaming_response_create(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.sessions.with_streaming_response.create( agent="agent_011CZkYpogX7uDKUyvBTophP", environment_id="env_011CZkZ9X2dpNyB7HsEFoRfW", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = await response.parse() assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_retrieve(self, async_client: AsyncAnthropic) -> None: session = await async_client.beta.sessions.retrieve( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) @parametrize async def test_method_retrieve_with_all_params(self, async_client: AsyncAnthropic) -> None: session = await async_client.beta.sessions.retrieve( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", betas=["string"], ) assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.sessions.with_raw_response.retrieve( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.sessions.with_streaming_response.retrieve( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = await response.parse() assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_retrieve(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): await async_client.beta.sessions.with_raw_response.retrieve( session_id="", ) @parametrize async def test_method_update(self, async_client: AsyncAnthropic) -> None: session = await async_client.beta.sessions.update( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) @parametrize async def test_method_update_with_all_params(self, async_client: AsyncAnthropic) -> None: session = await async_client.beta.sessions.update( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", agent={ "mcp_servers": [ { "name": "example-mcp", "type": "url", "url": "https://example-server.modelcontextprotocol.io/sse", } ], "tools": [ { "type": "agent_toolset_20260401", "configs": [ { "name": "bash", "enabled": True, "permission_policy": {"type": "always_allow"}, } ], "default_config": { "enabled": True, "permission_policy": {"type": "always_allow"}, }, } ], }, metadata={"foo": "string"}, title="Order #1234 inquiry", vault_ids=["string"], betas=["string"], ) assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) @parametrize async def test_raw_response_update(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.sessions.with_raw_response.update( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) @parametrize async def test_streaming_response_update(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.sessions.with_streaming_response.update( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = await response.parse() assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_update(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): await async_client.beta.sessions.with_raw_response.update( session_id="", ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list(self, async_client: AsyncAnthropic) -> None: session = await async_client.beta.sessions.list() assert_matches_type(AsyncBidirectionalPageCursor[BetaManagedAgentsSession], session, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncAnthropic) -> None: session = await async_client.beta.sessions.list( agent_id="agent_id", agent_version=0, created_at_gt=parse_datetime("2019-12-27T18:11:19.117Z"), created_at_gte=parse_datetime("2019-12-27T18:11:19.117Z"), created_at_lt=parse_datetime("2019-12-27T18:11:19.117Z"), created_at_lte=parse_datetime("2019-12-27T18:11:19.117Z"), deployment_id="deployment_id", include_archived=True, limit=0, memory_store_id="memory_store_id", order="asc", page="page", statuses=["rescheduling"], betas=["string"], ) assert_matches_type(AsyncBidirectionalPageCursor[BetaManagedAgentsSession], session, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_raw_response_list(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.sessions.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() assert_matches_type(AsyncBidirectionalPageCursor[BetaManagedAgentsSession], session, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_streaming_response_list(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.sessions.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = await response.parse() assert_matches_type(AsyncBidirectionalPageCursor[BetaManagedAgentsSession], session, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_delete(self, async_client: AsyncAnthropic) -> None: session = await async_client.beta.sessions.delete( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert_matches_type(BetaManagedAgentsDeletedSession, session, path=["response"]) @parametrize async def test_method_delete_with_all_params(self, async_client: AsyncAnthropic) -> None: session = await async_client.beta.sessions.delete( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", betas=["string"], ) assert_matches_type(BetaManagedAgentsDeletedSession, session, path=["response"]) @parametrize async def test_raw_response_delete(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.sessions.with_raw_response.delete( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() assert_matches_type(BetaManagedAgentsDeletedSession, session, path=["response"]) @parametrize async def test_streaming_response_delete(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.sessions.with_streaming_response.delete( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = await response.parse() assert_matches_type(BetaManagedAgentsDeletedSession, session, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_delete(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): await async_client.beta.sessions.with_raw_response.delete( session_id="", ) @parametrize async def test_method_archive(self, async_client: AsyncAnthropic) -> None: session = await async_client.beta.sessions.archive( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) @parametrize async def test_method_archive_with_all_params(self, async_client: AsyncAnthropic) -> None: session = await async_client.beta.sessions.archive( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", betas=["string"], ) assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) @parametrize async def test_raw_response_archive(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.sessions.with_raw_response.archive( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = response.parse() assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) @parametrize async def test_streaming_response_archive(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.sessions.with_streaming_response.archive( session_id="sesn_011CZkZAtmR3yMPDzynEDxu7", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" session = await response.parse() assert_matches_type(BetaManagedAgentsSession, session, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_archive(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `session_id` but received ''"): await async_client.beta.sessions.with_raw_response.archive( session_id="", ) anthropic-sdk-python-0.120.2/tests/api_resources/beta/test_skills.py000066400000000000000000000340031523216435200255760ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os from typing import Any, cast import pytest from anthropic import Anthropic, AsyncAnthropic from tests.utils import assert_matches_type from anthropic.pagination import SyncPageCursor, AsyncPageCursor from anthropic.types.beta import ( SkillListResponse, SkillCreateResponse, SkillDeleteResponse, SkillRetrieveResponse, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestSkills: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create(self, client: Anthropic) -> None: skill = client.beta.skills.create( files=[b"Example data"], ) assert_matches_type(SkillCreateResponse, skill, path=["response"]) @parametrize def test_method_create_with_all_params(self, client: Anthropic) -> None: skill = client.beta.skills.create( files=[b"Example data"], display_title="display_title", betas=["string"], ) assert_matches_type(SkillCreateResponse, skill, path=["response"]) @parametrize def test_raw_response_create(self, client: Anthropic) -> None: response = client.beta.skills.with_raw_response.create( files=[b"Example data"], ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" skill = response.parse() assert_matches_type(SkillCreateResponse, skill, path=["response"]) @parametrize def test_streaming_response_create(self, client: Anthropic) -> None: with client.beta.skills.with_streaming_response.create( files=[b"Example data"], ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" skill = response.parse() assert_matches_type(SkillCreateResponse, skill, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_retrieve(self, client: Anthropic) -> None: skill = client.beta.skills.retrieve( skill_id="skill_id", ) assert_matches_type(SkillRetrieveResponse, skill, path=["response"]) @parametrize def test_method_retrieve_with_all_params(self, client: Anthropic) -> None: skill = client.beta.skills.retrieve( skill_id="skill_id", betas=["string"], ) assert_matches_type(SkillRetrieveResponse, skill, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: Anthropic) -> None: response = client.beta.skills.with_raw_response.retrieve( skill_id="skill_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" skill = response.parse() assert_matches_type(SkillRetrieveResponse, skill, path=["response"]) @parametrize def test_streaming_response_retrieve(self, client: Anthropic) -> None: with client.beta.skills.with_streaming_response.retrieve( skill_id="skill_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" skill = response.parse() assert_matches_type(SkillRetrieveResponse, skill, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_retrieve(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): client.beta.skills.with_raw_response.retrieve( skill_id="", ) @parametrize def test_method_list(self, client: Anthropic) -> None: skill = client.beta.skills.list() assert_matches_type(SyncPageCursor[SkillListResponse], skill, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: Anthropic) -> None: skill = client.beta.skills.list( limit=0, page="page", source="source", betas=["string"], ) assert_matches_type(SyncPageCursor[SkillListResponse], skill, path=["response"]) @parametrize def test_raw_response_list(self, client: Anthropic) -> None: response = client.beta.skills.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" skill = response.parse() assert_matches_type(SyncPageCursor[SkillListResponse], skill, path=["response"]) @parametrize def test_streaming_response_list(self, client: Anthropic) -> None: with client.beta.skills.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" skill = response.parse() assert_matches_type(SyncPageCursor[SkillListResponse], skill, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_delete(self, client: Anthropic) -> None: skill = client.beta.skills.delete( skill_id="skill_id", ) assert_matches_type(SkillDeleteResponse, skill, path=["response"]) @parametrize def test_method_delete_with_all_params(self, client: Anthropic) -> None: skill = client.beta.skills.delete( skill_id="skill_id", betas=["string"], ) assert_matches_type(SkillDeleteResponse, skill, path=["response"]) @parametrize def test_raw_response_delete(self, client: Anthropic) -> None: response = client.beta.skills.with_raw_response.delete( skill_id="skill_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" skill = response.parse() assert_matches_type(SkillDeleteResponse, skill, path=["response"]) @parametrize def test_streaming_response_delete(self, client: Anthropic) -> None: with client.beta.skills.with_streaming_response.delete( skill_id="skill_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" skill = response.parse() assert_matches_type(SkillDeleteResponse, skill, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_delete(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): client.beta.skills.with_raw_response.delete( skill_id="", ) class TestAsyncSkills: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize async def test_method_create(self, async_client: AsyncAnthropic) -> None: skill = await async_client.beta.skills.create( files=[b"Example data"], ) assert_matches_type(SkillCreateResponse, skill, path=["response"]) @parametrize async def test_method_create_with_all_params(self, async_client: AsyncAnthropic) -> None: skill = await async_client.beta.skills.create( files=[b"Example data"], display_title="display_title", betas=["string"], ) assert_matches_type(SkillCreateResponse, skill, path=["response"]) @parametrize async def test_raw_response_create(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.skills.with_raw_response.create( files=[b"Example data"], ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" skill = response.parse() assert_matches_type(SkillCreateResponse, skill, path=["response"]) @parametrize async def test_streaming_response_create(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.skills.with_streaming_response.create( files=[b"Example data"], ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" skill = await response.parse() assert_matches_type(SkillCreateResponse, skill, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_retrieve(self, async_client: AsyncAnthropic) -> None: skill = await async_client.beta.skills.retrieve( skill_id="skill_id", ) assert_matches_type(SkillRetrieveResponse, skill, path=["response"]) @parametrize async def test_method_retrieve_with_all_params(self, async_client: AsyncAnthropic) -> None: skill = await async_client.beta.skills.retrieve( skill_id="skill_id", betas=["string"], ) assert_matches_type(SkillRetrieveResponse, skill, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.skills.with_raw_response.retrieve( skill_id="skill_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" skill = response.parse() assert_matches_type(SkillRetrieveResponse, skill, path=["response"]) @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.skills.with_streaming_response.retrieve( skill_id="skill_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" skill = await response.parse() assert_matches_type(SkillRetrieveResponse, skill, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_retrieve(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): await async_client.beta.skills.with_raw_response.retrieve( skill_id="", ) @parametrize async def test_method_list(self, async_client: AsyncAnthropic) -> None: skill = await async_client.beta.skills.list() assert_matches_type(AsyncPageCursor[SkillListResponse], skill, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncAnthropic) -> None: skill = await async_client.beta.skills.list( limit=0, page="page", source="source", betas=["string"], ) assert_matches_type(AsyncPageCursor[SkillListResponse], skill, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.skills.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" skill = response.parse() assert_matches_type(AsyncPageCursor[SkillListResponse], skill, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.skills.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" skill = await response.parse() assert_matches_type(AsyncPageCursor[SkillListResponse], skill, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_delete(self, async_client: AsyncAnthropic) -> None: skill = await async_client.beta.skills.delete( skill_id="skill_id", ) assert_matches_type(SkillDeleteResponse, skill, path=["response"]) @parametrize async def test_method_delete_with_all_params(self, async_client: AsyncAnthropic) -> None: skill = await async_client.beta.skills.delete( skill_id="skill_id", betas=["string"], ) assert_matches_type(SkillDeleteResponse, skill, path=["response"]) @parametrize async def test_raw_response_delete(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.skills.with_raw_response.delete( skill_id="skill_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" skill = response.parse() assert_matches_type(SkillDeleteResponse, skill, path=["response"]) @parametrize async def test_streaming_response_delete(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.skills.with_streaming_response.delete( skill_id="skill_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" skill = await response.parse() assert_matches_type(SkillDeleteResponse, skill, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_delete(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `skill_id` but received ''"): await async_client.beta.skills.with_raw_response.delete( skill_id="", ) anthropic-sdk-python-0.120.2/tests/api_resources/beta/test_tunnels.py000066400000000000000000000551361523216435200257770ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os from typing import Any, cast import pytest from anthropic import Anthropic, AsyncAnthropic from tests.utils import assert_matches_type from anthropic.pagination import SyncPageCursor, AsyncPageCursor from anthropic.types.beta import ( BetaTunnel, BetaTunnelToken, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestTunnels: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create(self, client: Anthropic) -> None: tunnel = client.beta.tunnels.create() assert_matches_type(BetaTunnel, tunnel, path=["response"]) @parametrize def test_method_create_with_all_params(self, client: Anthropic) -> None: tunnel = client.beta.tunnels.create( display_name="x", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaTunnel, tunnel, path=["response"]) @parametrize def test_raw_response_create(self, client: Anthropic) -> None: response = client.beta.tunnels.with_raw_response.create() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" tunnel = response.parse() assert_matches_type(BetaTunnel, tunnel, path=["response"]) @parametrize def test_streaming_response_create(self, client: Anthropic) -> None: with client.beta.tunnels.with_streaming_response.create() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" tunnel = response.parse() assert_matches_type(BetaTunnel, tunnel, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_retrieve(self, client: Anthropic) -> None: tunnel = client.beta.tunnels.retrieve( tunnel_id="tunnel_id", ) assert_matches_type(BetaTunnel, tunnel, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_retrieve_with_all_params(self, client: Anthropic) -> None: tunnel = client.beta.tunnels.retrieve( tunnel_id="tunnel_id", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaTunnel, tunnel, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_raw_response_retrieve(self, client: Anthropic) -> None: response = client.beta.tunnels.with_raw_response.retrieve( tunnel_id="tunnel_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" tunnel = response.parse() assert_matches_type(BetaTunnel, tunnel, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_streaming_response_retrieve(self, client: Anthropic) -> None: with client.beta.tunnels.with_streaming_response.retrieve( tunnel_id="tunnel_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" tunnel = response.parse() assert_matches_type(BetaTunnel, tunnel, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_path_params_retrieve(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `tunnel_id` but received ''"): client.beta.tunnels.with_raw_response.retrieve( tunnel_id="", ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list(self, client: Anthropic) -> None: tunnel = client.beta.tunnels.list() assert_matches_type(SyncPageCursor[BetaTunnel], tunnel, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list_with_all_params(self, client: Anthropic) -> None: tunnel = client.beta.tunnels.list( include_archived=True, limit=0, page="page", betas=["message-batches-2024-09-24"], ) assert_matches_type(SyncPageCursor[BetaTunnel], tunnel, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_raw_response_list(self, client: Anthropic) -> None: response = client.beta.tunnels.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" tunnel = response.parse() assert_matches_type(SyncPageCursor[BetaTunnel], tunnel, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_streaming_response_list(self, client: Anthropic) -> None: with client.beta.tunnels.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" tunnel = response.parse() assert_matches_type(SyncPageCursor[BetaTunnel], tunnel, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_archive(self, client: Anthropic) -> None: tunnel = client.beta.tunnels.archive( tunnel_id="tunnel_id", ) assert_matches_type(BetaTunnel, tunnel, path=["response"]) @parametrize def test_method_archive_with_all_params(self, client: Anthropic) -> None: tunnel = client.beta.tunnels.archive( tunnel_id="tunnel_id", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaTunnel, tunnel, path=["response"]) @parametrize def test_raw_response_archive(self, client: Anthropic) -> None: response = client.beta.tunnels.with_raw_response.archive( tunnel_id="tunnel_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" tunnel = response.parse() assert_matches_type(BetaTunnel, tunnel, path=["response"]) @parametrize def test_streaming_response_archive(self, client: Anthropic) -> None: with client.beta.tunnels.with_streaming_response.archive( tunnel_id="tunnel_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" tunnel = response.parse() assert_matches_type(BetaTunnel, tunnel, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_archive(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `tunnel_id` but received ''"): client.beta.tunnels.with_raw_response.archive( tunnel_id="", ) @parametrize def test_method_reveal_token(self, client: Anthropic) -> None: tunnel = client.beta.tunnels.reveal_token( tunnel_id="tunnel_id", ) assert_matches_type(BetaTunnelToken, tunnel, path=["response"]) @parametrize def test_method_reveal_token_with_all_params(self, client: Anthropic) -> None: tunnel = client.beta.tunnels.reveal_token( tunnel_id="tunnel_id", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaTunnelToken, tunnel, path=["response"]) @parametrize def test_raw_response_reveal_token(self, client: Anthropic) -> None: response = client.beta.tunnels.with_raw_response.reveal_token( tunnel_id="tunnel_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" tunnel = response.parse() assert_matches_type(BetaTunnelToken, tunnel, path=["response"]) @parametrize def test_streaming_response_reveal_token(self, client: Anthropic) -> None: with client.beta.tunnels.with_streaming_response.reveal_token( tunnel_id="tunnel_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" tunnel = response.parse() assert_matches_type(BetaTunnelToken, tunnel, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_reveal_token(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `tunnel_id` but received ''"): client.beta.tunnels.with_raw_response.reveal_token( tunnel_id="", ) @parametrize def test_method_rotate_token(self, client: Anthropic) -> None: tunnel = client.beta.tunnels.rotate_token( tunnel_id="tunnel_id", ) assert_matches_type(BetaTunnelToken, tunnel, path=["response"]) @parametrize def test_method_rotate_token_with_all_params(self, client: Anthropic) -> None: tunnel = client.beta.tunnels.rotate_token( tunnel_id="tunnel_id", reason="reason", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaTunnelToken, tunnel, path=["response"]) @parametrize def test_raw_response_rotate_token(self, client: Anthropic) -> None: response = client.beta.tunnels.with_raw_response.rotate_token( tunnel_id="tunnel_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" tunnel = response.parse() assert_matches_type(BetaTunnelToken, tunnel, path=["response"]) @parametrize def test_streaming_response_rotate_token(self, client: Anthropic) -> None: with client.beta.tunnels.with_streaming_response.rotate_token( tunnel_id="tunnel_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" tunnel = response.parse() assert_matches_type(BetaTunnelToken, tunnel, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_rotate_token(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `tunnel_id` but received ''"): client.beta.tunnels.with_raw_response.rotate_token( tunnel_id="", ) class TestAsyncTunnels: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize async def test_method_create(self, async_client: AsyncAnthropic) -> None: tunnel = await async_client.beta.tunnels.create() assert_matches_type(BetaTunnel, tunnel, path=["response"]) @parametrize async def test_method_create_with_all_params(self, async_client: AsyncAnthropic) -> None: tunnel = await async_client.beta.tunnels.create( display_name="x", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaTunnel, tunnel, path=["response"]) @parametrize async def test_raw_response_create(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.tunnels.with_raw_response.create() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" tunnel = response.parse() assert_matches_type(BetaTunnel, tunnel, path=["response"]) @parametrize async def test_streaming_response_create(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.tunnels.with_streaming_response.create() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" tunnel = await response.parse() assert_matches_type(BetaTunnel, tunnel, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_retrieve(self, async_client: AsyncAnthropic) -> None: tunnel = await async_client.beta.tunnels.retrieve( tunnel_id="tunnel_id", ) assert_matches_type(BetaTunnel, tunnel, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_retrieve_with_all_params(self, async_client: AsyncAnthropic) -> None: tunnel = await async_client.beta.tunnels.retrieve( tunnel_id="tunnel_id", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaTunnel, tunnel, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.tunnels.with_raw_response.retrieve( tunnel_id="tunnel_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" tunnel = response.parse() assert_matches_type(BetaTunnel, tunnel, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.tunnels.with_streaming_response.retrieve( tunnel_id="tunnel_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" tunnel = await response.parse() assert_matches_type(BetaTunnel, tunnel, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_path_params_retrieve(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `tunnel_id` but received ''"): await async_client.beta.tunnels.with_raw_response.retrieve( tunnel_id="", ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list(self, async_client: AsyncAnthropic) -> None: tunnel = await async_client.beta.tunnels.list() assert_matches_type(AsyncPageCursor[BetaTunnel], tunnel, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncAnthropic) -> None: tunnel = await async_client.beta.tunnels.list( include_archived=True, limit=0, page="page", betas=["message-batches-2024-09-24"], ) assert_matches_type(AsyncPageCursor[BetaTunnel], tunnel, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_raw_response_list(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.tunnels.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" tunnel = response.parse() assert_matches_type(AsyncPageCursor[BetaTunnel], tunnel, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_streaming_response_list(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.tunnels.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" tunnel = await response.parse() assert_matches_type(AsyncPageCursor[BetaTunnel], tunnel, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_archive(self, async_client: AsyncAnthropic) -> None: tunnel = await async_client.beta.tunnels.archive( tunnel_id="tunnel_id", ) assert_matches_type(BetaTunnel, tunnel, path=["response"]) @parametrize async def test_method_archive_with_all_params(self, async_client: AsyncAnthropic) -> None: tunnel = await async_client.beta.tunnels.archive( tunnel_id="tunnel_id", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaTunnel, tunnel, path=["response"]) @parametrize async def test_raw_response_archive(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.tunnels.with_raw_response.archive( tunnel_id="tunnel_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" tunnel = response.parse() assert_matches_type(BetaTunnel, tunnel, path=["response"]) @parametrize async def test_streaming_response_archive(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.tunnels.with_streaming_response.archive( tunnel_id="tunnel_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" tunnel = await response.parse() assert_matches_type(BetaTunnel, tunnel, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_archive(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `tunnel_id` but received ''"): await async_client.beta.tunnels.with_raw_response.archive( tunnel_id="", ) @parametrize async def test_method_reveal_token(self, async_client: AsyncAnthropic) -> None: tunnel = await async_client.beta.tunnels.reveal_token( tunnel_id="tunnel_id", ) assert_matches_type(BetaTunnelToken, tunnel, path=["response"]) @parametrize async def test_method_reveal_token_with_all_params(self, async_client: AsyncAnthropic) -> None: tunnel = await async_client.beta.tunnels.reveal_token( tunnel_id="tunnel_id", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaTunnelToken, tunnel, path=["response"]) @parametrize async def test_raw_response_reveal_token(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.tunnels.with_raw_response.reveal_token( tunnel_id="tunnel_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" tunnel = response.parse() assert_matches_type(BetaTunnelToken, tunnel, path=["response"]) @parametrize async def test_streaming_response_reveal_token(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.tunnels.with_streaming_response.reveal_token( tunnel_id="tunnel_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" tunnel = await response.parse() assert_matches_type(BetaTunnelToken, tunnel, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_reveal_token(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `tunnel_id` but received ''"): await async_client.beta.tunnels.with_raw_response.reveal_token( tunnel_id="", ) @parametrize async def test_method_rotate_token(self, async_client: AsyncAnthropic) -> None: tunnel = await async_client.beta.tunnels.rotate_token( tunnel_id="tunnel_id", ) assert_matches_type(BetaTunnelToken, tunnel, path=["response"]) @parametrize async def test_method_rotate_token_with_all_params(self, async_client: AsyncAnthropic) -> None: tunnel = await async_client.beta.tunnels.rotate_token( tunnel_id="tunnel_id", reason="reason", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaTunnelToken, tunnel, path=["response"]) @parametrize async def test_raw_response_rotate_token(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.tunnels.with_raw_response.rotate_token( tunnel_id="tunnel_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" tunnel = response.parse() assert_matches_type(BetaTunnelToken, tunnel, path=["response"]) @parametrize async def test_streaming_response_rotate_token(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.tunnels.with_streaming_response.rotate_token( tunnel_id="tunnel_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" tunnel = await response.parse() assert_matches_type(BetaTunnelToken, tunnel, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_rotate_token(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `tunnel_id` but received ''"): await async_client.beta.tunnels.with_raw_response.rotate_token( tunnel_id="", ) anthropic-sdk-python-0.120.2/tests/api_resources/beta/test_user_profiles.py000066400000000000000000000472531523216435200271710ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os from typing import Any, cast import pytest from anthropic import Anthropic, AsyncAnthropic from tests.utils import assert_matches_type from anthropic.pagination import SyncPageCursor, AsyncPageCursor from anthropic.types.beta import ( BetaUserProfile, BetaUserProfileEnrollmentURL, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestUserProfiles: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create(self, client: Anthropic) -> None: user_profile = client.beta.user_profiles.create() assert_matches_type(BetaUserProfile, user_profile, path=["response"]) @parametrize def test_method_create_with_all_params(self, client: Anthropic) -> None: user_profile = client.beta.user_profiles.create( external_id="user_12345", metadata={}, name="x", relationship="external", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaUserProfile, user_profile, path=["response"]) @parametrize def test_raw_response_create(self, client: Anthropic) -> None: response = client.beta.user_profiles.with_raw_response.create() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" user_profile = response.parse() assert_matches_type(BetaUserProfile, user_profile, path=["response"]) @parametrize def test_streaming_response_create(self, client: Anthropic) -> None: with client.beta.user_profiles.with_streaming_response.create() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" user_profile = response.parse() assert_matches_type(BetaUserProfile, user_profile, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_retrieve(self, client: Anthropic) -> None: user_profile = client.beta.user_profiles.retrieve( user_profile_id="uprof_011CZkZCu8hGbp5mYRQgUmz9", ) assert_matches_type(BetaUserProfile, user_profile, path=["response"]) @parametrize def test_method_retrieve_with_all_params(self, client: Anthropic) -> None: user_profile = client.beta.user_profiles.retrieve( user_profile_id="uprof_011CZkZCu8hGbp5mYRQgUmz9", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaUserProfile, user_profile, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: Anthropic) -> None: response = client.beta.user_profiles.with_raw_response.retrieve( user_profile_id="uprof_011CZkZCu8hGbp5mYRQgUmz9", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" user_profile = response.parse() assert_matches_type(BetaUserProfile, user_profile, path=["response"]) @parametrize def test_streaming_response_retrieve(self, client: Anthropic) -> None: with client.beta.user_profiles.with_streaming_response.retrieve( user_profile_id="uprof_011CZkZCu8hGbp5mYRQgUmz9", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" user_profile = response.parse() assert_matches_type(BetaUserProfile, user_profile, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_retrieve(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_profile_id` but received ''"): client.beta.user_profiles.with_raw_response.retrieve( user_profile_id="", ) @parametrize def test_method_update(self, client: Anthropic) -> None: user_profile = client.beta.user_profiles.update( user_profile_id="uprof_011CZkZCu8hGbp5mYRQgUmz9", ) assert_matches_type(BetaUserProfile, user_profile, path=["response"]) @parametrize def test_method_update_with_all_params(self, client: Anthropic) -> None: user_profile = client.beta.user_profiles.update( user_profile_id="uprof_011CZkZCu8hGbp5mYRQgUmz9", external_id="user_12345", metadata={"foo": "string"}, name="x", relationship="external", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaUserProfile, user_profile, path=["response"]) @parametrize def test_raw_response_update(self, client: Anthropic) -> None: response = client.beta.user_profiles.with_raw_response.update( user_profile_id="uprof_011CZkZCu8hGbp5mYRQgUmz9", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" user_profile = response.parse() assert_matches_type(BetaUserProfile, user_profile, path=["response"]) @parametrize def test_streaming_response_update(self, client: Anthropic) -> None: with client.beta.user_profiles.with_streaming_response.update( user_profile_id="uprof_011CZkZCu8hGbp5mYRQgUmz9", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" user_profile = response.parse() assert_matches_type(BetaUserProfile, user_profile, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_update(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_profile_id` but received ''"): client.beta.user_profiles.with_raw_response.update( user_profile_id="", ) @parametrize def test_method_list(self, client: Anthropic) -> None: user_profile = client.beta.user_profiles.list() assert_matches_type(SyncPageCursor[BetaUserProfile], user_profile, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: Anthropic) -> None: user_profile = client.beta.user_profiles.list( limit=0, order="asc", page="page", betas=["message-batches-2024-09-24"], ) assert_matches_type(SyncPageCursor[BetaUserProfile], user_profile, path=["response"]) @parametrize def test_raw_response_list(self, client: Anthropic) -> None: response = client.beta.user_profiles.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" user_profile = response.parse() assert_matches_type(SyncPageCursor[BetaUserProfile], user_profile, path=["response"]) @parametrize def test_streaming_response_list(self, client: Anthropic) -> None: with client.beta.user_profiles.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" user_profile = response.parse() assert_matches_type(SyncPageCursor[BetaUserProfile], user_profile, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_create_enrollment_url(self, client: Anthropic) -> None: user_profile = client.beta.user_profiles.create_enrollment_url( user_profile_id="uprof_011CZkZCu8hGbp5mYRQgUmz9", ) assert_matches_type(BetaUserProfileEnrollmentURL, user_profile, path=["response"]) @parametrize def test_method_create_enrollment_url_with_all_params(self, client: Anthropic) -> None: user_profile = client.beta.user_profiles.create_enrollment_url( user_profile_id="uprof_011CZkZCu8hGbp5mYRQgUmz9", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaUserProfileEnrollmentURL, user_profile, path=["response"]) @parametrize def test_raw_response_create_enrollment_url(self, client: Anthropic) -> None: response = client.beta.user_profiles.with_raw_response.create_enrollment_url( user_profile_id="uprof_011CZkZCu8hGbp5mYRQgUmz9", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" user_profile = response.parse() assert_matches_type(BetaUserProfileEnrollmentURL, user_profile, path=["response"]) @parametrize def test_streaming_response_create_enrollment_url(self, client: Anthropic) -> None: with client.beta.user_profiles.with_streaming_response.create_enrollment_url( user_profile_id="uprof_011CZkZCu8hGbp5mYRQgUmz9", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" user_profile = response.parse() assert_matches_type(BetaUserProfileEnrollmentURL, user_profile, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_create_enrollment_url(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_profile_id` but received ''"): client.beta.user_profiles.with_raw_response.create_enrollment_url( user_profile_id="", ) class TestAsyncUserProfiles: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize async def test_method_create(self, async_client: AsyncAnthropic) -> None: user_profile = await async_client.beta.user_profiles.create() assert_matches_type(BetaUserProfile, user_profile, path=["response"]) @parametrize async def test_method_create_with_all_params(self, async_client: AsyncAnthropic) -> None: user_profile = await async_client.beta.user_profiles.create( external_id="user_12345", metadata={}, name="x", relationship="external", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaUserProfile, user_profile, path=["response"]) @parametrize async def test_raw_response_create(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.user_profiles.with_raw_response.create() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" user_profile = response.parse() assert_matches_type(BetaUserProfile, user_profile, path=["response"]) @parametrize async def test_streaming_response_create(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.user_profiles.with_streaming_response.create() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" user_profile = await response.parse() assert_matches_type(BetaUserProfile, user_profile, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_retrieve(self, async_client: AsyncAnthropic) -> None: user_profile = await async_client.beta.user_profiles.retrieve( user_profile_id="uprof_011CZkZCu8hGbp5mYRQgUmz9", ) assert_matches_type(BetaUserProfile, user_profile, path=["response"]) @parametrize async def test_method_retrieve_with_all_params(self, async_client: AsyncAnthropic) -> None: user_profile = await async_client.beta.user_profiles.retrieve( user_profile_id="uprof_011CZkZCu8hGbp5mYRQgUmz9", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaUserProfile, user_profile, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.user_profiles.with_raw_response.retrieve( user_profile_id="uprof_011CZkZCu8hGbp5mYRQgUmz9", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" user_profile = response.parse() assert_matches_type(BetaUserProfile, user_profile, path=["response"]) @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.user_profiles.with_streaming_response.retrieve( user_profile_id="uprof_011CZkZCu8hGbp5mYRQgUmz9", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" user_profile = await response.parse() assert_matches_type(BetaUserProfile, user_profile, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_retrieve(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_profile_id` but received ''"): await async_client.beta.user_profiles.with_raw_response.retrieve( user_profile_id="", ) @parametrize async def test_method_update(self, async_client: AsyncAnthropic) -> None: user_profile = await async_client.beta.user_profiles.update( user_profile_id="uprof_011CZkZCu8hGbp5mYRQgUmz9", ) assert_matches_type(BetaUserProfile, user_profile, path=["response"]) @parametrize async def test_method_update_with_all_params(self, async_client: AsyncAnthropic) -> None: user_profile = await async_client.beta.user_profiles.update( user_profile_id="uprof_011CZkZCu8hGbp5mYRQgUmz9", external_id="user_12345", metadata={"foo": "string"}, name="x", relationship="external", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaUserProfile, user_profile, path=["response"]) @parametrize async def test_raw_response_update(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.user_profiles.with_raw_response.update( user_profile_id="uprof_011CZkZCu8hGbp5mYRQgUmz9", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" user_profile = response.parse() assert_matches_type(BetaUserProfile, user_profile, path=["response"]) @parametrize async def test_streaming_response_update(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.user_profiles.with_streaming_response.update( user_profile_id="uprof_011CZkZCu8hGbp5mYRQgUmz9", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" user_profile = await response.parse() assert_matches_type(BetaUserProfile, user_profile, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_update(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_profile_id` but received ''"): await async_client.beta.user_profiles.with_raw_response.update( user_profile_id="", ) @parametrize async def test_method_list(self, async_client: AsyncAnthropic) -> None: user_profile = await async_client.beta.user_profiles.list() assert_matches_type(AsyncPageCursor[BetaUserProfile], user_profile, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncAnthropic) -> None: user_profile = await async_client.beta.user_profiles.list( limit=0, order="asc", page="page", betas=["message-batches-2024-09-24"], ) assert_matches_type(AsyncPageCursor[BetaUserProfile], user_profile, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.user_profiles.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" user_profile = response.parse() assert_matches_type(AsyncPageCursor[BetaUserProfile], user_profile, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.user_profiles.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" user_profile = await response.parse() assert_matches_type(AsyncPageCursor[BetaUserProfile], user_profile, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_create_enrollment_url(self, async_client: AsyncAnthropic) -> None: user_profile = await async_client.beta.user_profiles.create_enrollment_url( user_profile_id="uprof_011CZkZCu8hGbp5mYRQgUmz9", ) assert_matches_type(BetaUserProfileEnrollmentURL, user_profile, path=["response"]) @parametrize async def test_method_create_enrollment_url_with_all_params(self, async_client: AsyncAnthropic) -> None: user_profile = await async_client.beta.user_profiles.create_enrollment_url( user_profile_id="uprof_011CZkZCu8hGbp5mYRQgUmz9", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaUserProfileEnrollmentURL, user_profile, path=["response"]) @parametrize async def test_raw_response_create_enrollment_url(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.user_profiles.with_raw_response.create_enrollment_url( user_profile_id="uprof_011CZkZCu8hGbp5mYRQgUmz9", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" user_profile = response.parse() assert_matches_type(BetaUserProfileEnrollmentURL, user_profile, path=["response"]) @parametrize async def test_streaming_response_create_enrollment_url(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.user_profiles.with_streaming_response.create_enrollment_url( user_profile_id="uprof_011CZkZCu8hGbp5mYRQgUmz9", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" user_profile = await response.parse() assert_matches_type(BetaUserProfileEnrollmentURL, user_profile, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_create_enrollment_url(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `user_profile_id` but received ''"): await async_client.beta.user_profiles.with_raw_response.create_enrollment_url( user_profile_id="", ) anthropic-sdk-python-0.120.2/tests/api_resources/beta/test_vaults.py000066400000000000000000000555261523216435200256300ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os from typing import Any, cast import pytest from anthropic import Anthropic, AsyncAnthropic from tests.utils import assert_matches_type from anthropic.pagination import SyncPageCursor, AsyncPageCursor from anthropic.types.beta import ( BetaManagedAgentsVault, BetaManagedAgentsDeletedVault, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestVaults: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create(self, client: Anthropic) -> None: vault = client.beta.vaults.create( display_name="Example vault", ) assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) @parametrize def test_method_create_with_all_params(self, client: Anthropic) -> None: vault = client.beta.vaults.create( display_name="Example vault", metadata={"environment": "production"}, betas=["string"], ) assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) @parametrize def test_raw_response_create(self, client: Anthropic) -> None: response = client.beta.vaults.with_raw_response.create( display_name="Example vault", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" vault = response.parse() assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) @parametrize def test_streaming_response_create(self, client: Anthropic) -> None: with client.beta.vaults.with_streaming_response.create( display_name="Example vault", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" vault = response.parse() assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_retrieve(self, client: Anthropic) -> None: vault = client.beta.vaults.retrieve( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) @parametrize def test_method_retrieve_with_all_params(self, client: Anthropic) -> None: vault = client.beta.vaults.retrieve( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", betas=["string"], ) assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: Anthropic) -> None: response = client.beta.vaults.with_raw_response.retrieve( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" vault = response.parse() assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) @parametrize def test_streaming_response_retrieve(self, client: Anthropic) -> None: with client.beta.vaults.with_streaming_response.retrieve( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" vault = response.parse() assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_retrieve(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"): client.beta.vaults.with_raw_response.retrieve( vault_id="", ) @parametrize def test_method_update(self, client: Anthropic) -> None: vault = client.beta.vaults.update( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) @parametrize def test_method_update_with_all_params(self, client: Anthropic) -> None: vault = client.beta.vaults.update( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", display_name="Example vault", metadata={"environment": "production"}, betas=["string"], ) assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) @parametrize def test_raw_response_update(self, client: Anthropic) -> None: response = client.beta.vaults.with_raw_response.update( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" vault = response.parse() assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) @parametrize def test_streaming_response_update(self, client: Anthropic) -> None: with client.beta.vaults.with_streaming_response.update( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" vault = response.parse() assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_update(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"): client.beta.vaults.with_raw_response.update( vault_id="", ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list(self, client: Anthropic) -> None: vault = client.beta.vaults.list() assert_matches_type(SyncPageCursor[BetaManagedAgentsVault], vault, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list_with_all_params(self, client: Anthropic) -> None: vault = client.beta.vaults.list( include_archived=True, limit=0, page="page", betas=["string"], ) assert_matches_type(SyncPageCursor[BetaManagedAgentsVault], vault, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_raw_response_list(self, client: Anthropic) -> None: response = client.beta.vaults.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" vault = response.parse() assert_matches_type(SyncPageCursor[BetaManagedAgentsVault], vault, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_streaming_response_list(self, client: Anthropic) -> None: with client.beta.vaults.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" vault = response.parse() assert_matches_type(SyncPageCursor[BetaManagedAgentsVault], vault, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_delete(self, client: Anthropic) -> None: vault = client.beta.vaults.delete( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert_matches_type(BetaManagedAgentsDeletedVault, vault, path=["response"]) @parametrize def test_method_delete_with_all_params(self, client: Anthropic) -> None: vault = client.beta.vaults.delete( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", betas=["string"], ) assert_matches_type(BetaManagedAgentsDeletedVault, vault, path=["response"]) @parametrize def test_raw_response_delete(self, client: Anthropic) -> None: response = client.beta.vaults.with_raw_response.delete( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" vault = response.parse() assert_matches_type(BetaManagedAgentsDeletedVault, vault, path=["response"]) @parametrize def test_streaming_response_delete(self, client: Anthropic) -> None: with client.beta.vaults.with_streaming_response.delete( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" vault = response.parse() assert_matches_type(BetaManagedAgentsDeletedVault, vault, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_delete(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"): client.beta.vaults.with_raw_response.delete( vault_id="", ) @parametrize def test_method_archive(self, client: Anthropic) -> None: vault = client.beta.vaults.archive( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) @parametrize def test_method_archive_with_all_params(self, client: Anthropic) -> None: vault = client.beta.vaults.archive( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", betas=["string"], ) assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) @parametrize def test_raw_response_archive(self, client: Anthropic) -> None: response = client.beta.vaults.with_raw_response.archive( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" vault = response.parse() assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) @parametrize def test_streaming_response_archive(self, client: Anthropic) -> None: with client.beta.vaults.with_streaming_response.archive( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" vault = response.parse() assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_archive(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"): client.beta.vaults.with_raw_response.archive( vault_id="", ) class TestAsyncVaults: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize async def test_method_create(self, async_client: AsyncAnthropic) -> None: vault = await async_client.beta.vaults.create( display_name="Example vault", ) assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) @parametrize async def test_method_create_with_all_params(self, async_client: AsyncAnthropic) -> None: vault = await async_client.beta.vaults.create( display_name="Example vault", metadata={"environment": "production"}, betas=["string"], ) assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) @parametrize async def test_raw_response_create(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.vaults.with_raw_response.create( display_name="Example vault", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" vault = response.parse() assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) @parametrize async def test_streaming_response_create(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.vaults.with_streaming_response.create( display_name="Example vault", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" vault = await response.parse() assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_retrieve(self, async_client: AsyncAnthropic) -> None: vault = await async_client.beta.vaults.retrieve( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) @parametrize async def test_method_retrieve_with_all_params(self, async_client: AsyncAnthropic) -> None: vault = await async_client.beta.vaults.retrieve( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", betas=["string"], ) assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.vaults.with_raw_response.retrieve( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" vault = response.parse() assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.vaults.with_streaming_response.retrieve( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" vault = await response.parse() assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_retrieve(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"): await async_client.beta.vaults.with_raw_response.retrieve( vault_id="", ) @parametrize async def test_method_update(self, async_client: AsyncAnthropic) -> None: vault = await async_client.beta.vaults.update( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) @parametrize async def test_method_update_with_all_params(self, async_client: AsyncAnthropic) -> None: vault = await async_client.beta.vaults.update( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", display_name="Example vault", metadata={"environment": "production"}, betas=["string"], ) assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) @parametrize async def test_raw_response_update(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.vaults.with_raw_response.update( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" vault = response.parse() assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) @parametrize async def test_streaming_response_update(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.vaults.with_streaming_response.update( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" vault = await response.parse() assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_update(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"): await async_client.beta.vaults.with_raw_response.update( vault_id="", ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list(self, async_client: AsyncAnthropic) -> None: vault = await async_client.beta.vaults.list() assert_matches_type(AsyncPageCursor[BetaManagedAgentsVault], vault, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncAnthropic) -> None: vault = await async_client.beta.vaults.list( include_archived=True, limit=0, page="page", betas=["string"], ) assert_matches_type(AsyncPageCursor[BetaManagedAgentsVault], vault, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_raw_response_list(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.vaults.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" vault = response.parse() assert_matches_type(AsyncPageCursor[BetaManagedAgentsVault], vault, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_streaming_response_list(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.vaults.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" vault = await response.parse() assert_matches_type(AsyncPageCursor[BetaManagedAgentsVault], vault, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_delete(self, async_client: AsyncAnthropic) -> None: vault = await async_client.beta.vaults.delete( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert_matches_type(BetaManagedAgentsDeletedVault, vault, path=["response"]) @parametrize async def test_method_delete_with_all_params(self, async_client: AsyncAnthropic) -> None: vault = await async_client.beta.vaults.delete( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", betas=["string"], ) assert_matches_type(BetaManagedAgentsDeletedVault, vault, path=["response"]) @parametrize async def test_raw_response_delete(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.vaults.with_raw_response.delete( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" vault = response.parse() assert_matches_type(BetaManagedAgentsDeletedVault, vault, path=["response"]) @parametrize async def test_streaming_response_delete(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.vaults.with_streaming_response.delete( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" vault = await response.parse() assert_matches_type(BetaManagedAgentsDeletedVault, vault, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_delete(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"): await async_client.beta.vaults.with_raw_response.delete( vault_id="", ) @parametrize async def test_method_archive(self, async_client: AsyncAnthropic) -> None: vault = await async_client.beta.vaults.archive( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) @parametrize async def test_method_archive_with_all_params(self, async_client: AsyncAnthropic) -> None: vault = await async_client.beta.vaults.archive( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", betas=["string"], ) assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) @parametrize async def test_raw_response_archive(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.vaults.with_raw_response.archive( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" vault = response.parse() assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) @parametrize async def test_streaming_response_archive(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.vaults.with_streaming_response.archive( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" vault = await response.parse() assert_matches_type(BetaManagedAgentsVault, vault, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_archive(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"): await async_client.beta.vaults.with_raw_response.archive( vault_id="", ) anthropic-sdk-python-0.120.2/tests/api_resources/beta/test_webhooks.py000066400000000000000000000102721523216435200261200ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os from datetime import datetime, timezone import pytest import standardwebhooks from anthropic import Anthropic base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestWebhooks: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @pytest.mark.parametrize( "client_opt,method_opt", [ ("whsec_c2VjcmV0Cg==", None), ("wrong", b"secret\n"), ("wrong", "whsec_c2VjcmV0Cg=="), (None, b"secret\n"), (None, "whsec_c2VjcmV0Cg=="), ], ) def test_method_unwrap(self, client: Anthropic, client_opt: str | None, method_opt: str | bytes | None) -> None: hook = standardwebhooks.Webhook(b"secret\n") client = client.with_options(webhook_key=client_opt) data = """{"id":"whe_0f1e2d3c4b5a69788796a5b4c3d2e1f0","created_at":"2026-03-15T10:00:00Z","data":{"id":"sesn_011CZkZAtmR3yMPDzynEDxu7","organization_id":"org_011CZkZZAe0sMna4vkBdtrfx","type":"session.status_idled","workspace_id":"wrkspc_011CZkZaBF1tNoB5wlCeusgy"},"type":"event"}""" msg_id = "1" timestamp = datetime.now(tz=timezone.utc) sig = hook.sign(msg_id=msg_id, timestamp=timestamp, data=data) headers = { "webhook-id": msg_id, "webhook-timestamp": str(int(timestamp.timestamp())), "webhook-signature": sig, } try: _ = client.beta.webhooks.unwrap(data, headers=headers, key=method_opt) except standardwebhooks.WebhookVerificationError as e: raise AssertionError("Failed to unwrap valid webhook") from e bad_headers = [ {**headers, "webhook-signature": hook.sign(msg_id=msg_id, timestamp=timestamp, data="xxx")}, {**headers, "webhook-id": "bad"}, {**headers, "webhook-timestamp": "0"}, ] for bad_header in bad_headers: with pytest.raises(standardwebhooks.WebhookVerificationError): _ = client.beta.webhooks.unwrap(data, headers=bad_header, key=method_opt) class TestAsyncWebhooks: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @pytest.mark.parametrize( "client_opt,method_opt", [ ("whsec_c2VjcmV0Cg==", None), ("wrong", b"secret\n"), ("wrong", "whsec_c2VjcmV0Cg=="), (None, b"secret\n"), (None, "whsec_c2VjcmV0Cg=="), ], ) def test_method_unwrap( self, async_client: Anthropic, client_opt: str | None, method_opt: str | bytes | None ) -> None: hook = standardwebhooks.Webhook(b"secret\n") async_client = async_client.with_options(webhook_key=client_opt) data = """{"id":"whe_0f1e2d3c4b5a69788796a5b4c3d2e1f0","created_at":"2026-03-15T10:00:00Z","data":{"id":"sesn_011CZkZAtmR3yMPDzynEDxu7","organization_id":"org_011CZkZZAe0sMna4vkBdtrfx","type":"session.status_idled","workspace_id":"wrkspc_011CZkZaBF1tNoB5wlCeusgy"},"type":"event"}""" msg_id = "1" timestamp = datetime.now(tz=timezone.utc) sig = hook.sign(msg_id=msg_id, timestamp=timestamp, data=data) headers = { "webhook-id": msg_id, "webhook-timestamp": str(int(timestamp.timestamp())), "webhook-signature": sig, } try: _ = async_client.beta.webhooks.unwrap(data, headers=headers, key=method_opt) except standardwebhooks.WebhookVerificationError as e: raise AssertionError("Failed to unwrap valid webhook") from e bad_headers = [ {**headers, "webhook-signature": hook.sign(msg_id=msg_id, timestamp=timestamp, data="xxx")}, {**headers, "webhook-id": "bad"}, {**headers, "webhook-timestamp": "0"}, ] for bad_header in bad_headers: with pytest.raises(standardwebhooks.WebhookVerificationError): _ = async_client.beta.webhooks.unwrap(data, headers=bad_header, key=method_opt) anthropic-sdk-python-0.120.2/tests/api_resources/beta/tunnels/000077500000000000000000000000001523216435200243545ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/api_resources/beta/tunnels/__init__.py000066400000000000000000000001261523216435200264640ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. anthropic-sdk-python-0.120.2/tests/api_resources/beta/tunnels/test_certificates.py000066400000000000000000000513131523216435200304350ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os from typing import Any, cast import pytest from anthropic import Anthropic, AsyncAnthropic from tests.utils import assert_matches_type from anthropic.pagination import SyncPageCursor, AsyncPageCursor from anthropic.types.beta.tunnels import BetaTunnelCertificate base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestCertificates: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create(self, client: Anthropic) -> None: certificate = client.beta.tunnels.certificates.create( tunnel_id="tunnel_id", ca_certificate_pem="ca_certificate_pem", ) assert_matches_type(BetaTunnelCertificate, certificate, path=["response"]) @parametrize def test_method_create_with_all_params(self, client: Anthropic) -> None: certificate = client.beta.tunnels.certificates.create( tunnel_id="tunnel_id", ca_certificate_pem="ca_certificate_pem", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaTunnelCertificate, certificate, path=["response"]) @parametrize def test_raw_response_create(self, client: Anthropic) -> None: response = client.beta.tunnels.certificates.with_raw_response.create( tunnel_id="tunnel_id", ca_certificate_pem="ca_certificate_pem", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = response.parse() assert_matches_type(BetaTunnelCertificate, certificate, path=["response"]) @parametrize def test_streaming_response_create(self, client: Anthropic) -> None: with client.beta.tunnels.certificates.with_streaming_response.create( tunnel_id="tunnel_id", ca_certificate_pem="ca_certificate_pem", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = response.parse() assert_matches_type(BetaTunnelCertificate, certificate, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_create(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `tunnel_id` but received ''"): client.beta.tunnels.certificates.with_raw_response.create( tunnel_id="", ca_certificate_pem="ca_certificate_pem", ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_retrieve(self, client: Anthropic) -> None: certificate = client.beta.tunnels.certificates.retrieve( certificate_id="certificate_id", tunnel_id="tunnel_id", ) assert_matches_type(BetaTunnelCertificate, certificate, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_retrieve_with_all_params(self, client: Anthropic) -> None: certificate = client.beta.tunnels.certificates.retrieve( certificate_id="certificate_id", tunnel_id="tunnel_id", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaTunnelCertificate, certificate, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_raw_response_retrieve(self, client: Anthropic) -> None: response = client.beta.tunnels.certificates.with_raw_response.retrieve( certificate_id="certificate_id", tunnel_id="tunnel_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = response.parse() assert_matches_type(BetaTunnelCertificate, certificate, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_streaming_response_retrieve(self, client: Anthropic) -> None: with client.beta.tunnels.certificates.with_streaming_response.retrieve( certificate_id="certificate_id", tunnel_id="tunnel_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = response.parse() assert_matches_type(BetaTunnelCertificate, certificate, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_path_params_retrieve(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `tunnel_id` but received ''"): client.beta.tunnels.certificates.with_raw_response.retrieve( certificate_id="certificate_id", tunnel_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `certificate_id` but received ''"): client.beta.tunnels.certificates.with_raw_response.retrieve( certificate_id="", tunnel_id="tunnel_id", ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list(self, client: Anthropic) -> None: certificate = client.beta.tunnels.certificates.list( tunnel_id="tunnel_id", ) assert_matches_type(SyncPageCursor[BetaTunnelCertificate], certificate, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list_with_all_params(self, client: Anthropic) -> None: certificate = client.beta.tunnels.certificates.list( tunnel_id="tunnel_id", include_archived=True, limit=0, page="page", betas=["message-batches-2024-09-24"], ) assert_matches_type(SyncPageCursor[BetaTunnelCertificate], certificate, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_raw_response_list(self, client: Anthropic) -> None: response = client.beta.tunnels.certificates.with_raw_response.list( tunnel_id="tunnel_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = response.parse() assert_matches_type(SyncPageCursor[BetaTunnelCertificate], certificate, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_streaming_response_list(self, client: Anthropic) -> None: with client.beta.tunnels.certificates.with_streaming_response.list( tunnel_id="tunnel_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = response.parse() assert_matches_type(SyncPageCursor[BetaTunnelCertificate], certificate, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_path_params_list(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `tunnel_id` but received ''"): client.beta.tunnels.certificates.with_raw_response.list( tunnel_id="", ) @parametrize def test_method_archive(self, client: Anthropic) -> None: certificate = client.beta.tunnels.certificates.archive( certificate_id="certificate_id", tunnel_id="tunnel_id", ) assert_matches_type(BetaTunnelCertificate, certificate, path=["response"]) @parametrize def test_method_archive_with_all_params(self, client: Anthropic) -> None: certificate = client.beta.tunnels.certificates.archive( certificate_id="certificate_id", tunnel_id="tunnel_id", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaTunnelCertificate, certificate, path=["response"]) @parametrize def test_raw_response_archive(self, client: Anthropic) -> None: response = client.beta.tunnels.certificates.with_raw_response.archive( certificate_id="certificate_id", tunnel_id="tunnel_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = response.parse() assert_matches_type(BetaTunnelCertificate, certificate, path=["response"]) @parametrize def test_streaming_response_archive(self, client: Anthropic) -> None: with client.beta.tunnels.certificates.with_streaming_response.archive( certificate_id="certificate_id", tunnel_id="tunnel_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = response.parse() assert_matches_type(BetaTunnelCertificate, certificate, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_archive(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `tunnel_id` but received ''"): client.beta.tunnels.certificates.with_raw_response.archive( certificate_id="certificate_id", tunnel_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `certificate_id` but received ''"): client.beta.tunnels.certificates.with_raw_response.archive( certificate_id="", tunnel_id="tunnel_id", ) class TestAsyncCertificates: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize async def test_method_create(self, async_client: AsyncAnthropic) -> None: certificate = await async_client.beta.tunnels.certificates.create( tunnel_id="tunnel_id", ca_certificate_pem="ca_certificate_pem", ) assert_matches_type(BetaTunnelCertificate, certificate, path=["response"]) @parametrize async def test_method_create_with_all_params(self, async_client: AsyncAnthropic) -> None: certificate = await async_client.beta.tunnels.certificates.create( tunnel_id="tunnel_id", ca_certificate_pem="ca_certificate_pem", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaTunnelCertificate, certificate, path=["response"]) @parametrize async def test_raw_response_create(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.tunnels.certificates.with_raw_response.create( tunnel_id="tunnel_id", ca_certificate_pem="ca_certificate_pem", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = response.parse() assert_matches_type(BetaTunnelCertificate, certificate, path=["response"]) @parametrize async def test_streaming_response_create(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.tunnels.certificates.with_streaming_response.create( tunnel_id="tunnel_id", ca_certificate_pem="ca_certificate_pem", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = await response.parse() assert_matches_type(BetaTunnelCertificate, certificate, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_create(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `tunnel_id` but received ''"): await async_client.beta.tunnels.certificates.with_raw_response.create( tunnel_id="", ca_certificate_pem="ca_certificate_pem", ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_retrieve(self, async_client: AsyncAnthropic) -> None: certificate = await async_client.beta.tunnels.certificates.retrieve( certificate_id="certificate_id", tunnel_id="tunnel_id", ) assert_matches_type(BetaTunnelCertificate, certificate, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_retrieve_with_all_params(self, async_client: AsyncAnthropic) -> None: certificate = await async_client.beta.tunnels.certificates.retrieve( certificate_id="certificate_id", tunnel_id="tunnel_id", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaTunnelCertificate, certificate, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_raw_response_retrieve(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.tunnels.certificates.with_raw_response.retrieve( certificate_id="certificate_id", tunnel_id="tunnel_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = response.parse() assert_matches_type(BetaTunnelCertificate, certificate, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.tunnels.certificates.with_streaming_response.retrieve( certificate_id="certificate_id", tunnel_id="tunnel_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = await response.parse() assert_matches_type(BetaTunnelCertificate, certificate, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_path_params_retrieve(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `tunnel_id` but received ''"): await async_client.beta.tunnels.certificates.with_raw_response.retrieve( certificate_id="certificate_id", tunnel_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `certificate_id` but received ''"): await async_client.beta.tunnels.certificates.with_raw_response.retrieve( certificate_id="", tunnel_id="tunnel_id", ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list(self, async_client: AsyncAnthropic) -> None: certificate = await async_client.beta.tunnels.certificates.list( tunnel_id="tunnel_id", ) assert_matches_type(AsyncPageCursor[BetaTunnelCertificate], certificate, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncAnthropic) -> None: certificate = await async_client.beta.tunnels.certificates.list( tunnel_id="tunnel_id", include_archived=True, limit=0, page="page", betas=["message-batches-2024-09-24"], ) assert_matches_type(AsyncPageCursor[BetaTunnelCertificate], certificate, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_raw_response_list(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.tunnels.certificates.with_raw_response.list( tunnel_id="tunnel_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = response.parse() assert_matches_type(AsyncPageCursor[BetaTunnelCertificate], certificate, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_streaming_response_list(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.tunnels.certificates.with_streaming_response.list( tunnel_id="tunnel_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = await response.parse() assert_matches_type(AsyncPageCursor[BetaTunnelCertificate], certificate, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_path_params_list(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `tunnel_id` but received ''"): await async_client.beta.tunnels.certificates.with_raw_response.list( tunnel_id="", ) @parametrize async def test_method_archive(self, async_client: AsyncAnthropic) -> None: certificate = await async_client.beta.tunnels.certificates.archive( certificate_id="certificate_id", tunnel_id="tunnel_id", ) assert_matches_type(BetaTunnelCertificate, certificate, path=["response"]) @parametrize async def test_method_archive_with_all_params(self, async_client: AsyncAnthropic) -> None: certificate = await async_client.beta.tunnels.certificates.archive( certificate_id="certificate_id", tunnel_id="tunnel_id", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaTunnelCertificate, certificate, path=["response"]) @parametrize async def test_raw_response_archive(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.tunnels.certificates.with_raw_response.archive( certificate_id="certificate_id", tunnel_id="tunnel_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = response.parse() assert_matches_type(BetaTunnelCertificate, certificate, path=["response"]) @parametrize async def test_streaming_response_archive(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.tunnels.certificates.with_streaming_response.archive( certificate_id="certificate_id", tunnel_id="tunnel_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" certificate = await response.parse() assert_matches_type(BetaTunnelCertificate, certificate, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_archive(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `tunnel_id` but received ''"): await async_client.beta.tunnels.certificates.with_raw_response.archive( certificate_id="certificate_id", tunnel_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `certificate_id` but received ''"): await async_client.beta.tunnels.certificates.with_raw_response.archive( certificate_id="", tunnel_id="tunnel_id", ) anthropic-sdk-python-0.120.2/tests/api_resources/beta/vaults/000077500000000000000000000000001523216435200242025ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/api_resources/beta/vaults/__init__.py000066400000000000000000000001261523216435200263120ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. anthropic-sdk-python-0.120.2/tests/api_resources/beta/vaults/test_credentials.py000066400000000000000000001204571523216435200301210ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os from typing import Any, cast import pytest from anthropic import Anthropic, AsyncAnthropic from tests.utils import assert_matches_type from anthropic._utils import parse_datetime from anthropic.pagination import SyncPageCursor, AsyncPageCursor from anthropic.types.beta.vaults import ( BetaManagedAgentsCredential, BetaManagedAgentsDeletedCredential, BetaManagedAgentsCredentialValidation, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestCredentials: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create(self, client: Anthropic) -> None: credential = client.beta.vaults.credentials.create( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", auth={ "token": "bearer_exampletoken", "mcp_server_url": "https://example-server.modelcontextprotocol.io/sse", "type": "static_bearer", }, ) assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) @parametrize def test_method_create_with_all_params(self, client: Anthropic) -> None: credential = client.beta.vaults.credentials.create( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", auth={ "token": "bearer_exampletoken", "mcp_server_url": "https://example-server.modelcontextprotocol.io/sse", "type": "static_bearer", }, display_name="Example credential", metadata={"environment": "production"}, betas=["string"], ) assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) @parametrize def test_raw_response_create(self, client: Anthropic) -> None: response = client.beta.vaults.credentials.with_raw_response.create( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", auth={ "token": "bearer_exampletoken", "mcp_server_url": "https://example-server.modelcontextprotocol.io/sse", "type": "static_bearer", }, ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = response.parse() assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) @parametrize def test_streaming_response_create(self, client: Anthropic) -> None: with client.beta.vaults.credentials.with_streaming_response.create( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", auth={ "token": "bearer_exampletoken", "mcp_server_url": "https://example-server.modelcontextprotocol.io/sse", "type": "static_bearer", }, ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = response.parse() assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_create(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"): client.beta.vaults.credentials.with_raw_response.create( vault_id="", auth={ "token": "bearer_exampletoken", "mcp_server_url": "https://example-server.modelcontextprotocol.io/sse", "type": "static_bearer", }, ) @parametrize def test_method_retrieve(self, client: Anthropic) -> None: credential = client.beta.vaults.credentials.retrieve( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) @parametrize def test_method_retrieve_with_all_params(self, client: Anthropic) -> None: credential = client.beta.vaults.credentials.retrieve( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", betas=["string"], ) assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: Anthropic) -> None: response = client.beta.vaults.credentials.with_raw_response.retrieve( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = response.parse() assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) @parametrize def test_streaming_response_retrieve(self, client: Anthropic) -> None: with client.beta.vaults.credentials.with_streaming_response.retrieve( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = response.parse() assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_retrieve(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"): client.beta.vaults.credentials.with_raw_response.retrieve( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `credential_id` but received ''"): client.beta.vaults.credentials.with_raw_response.retrieve( credential_id="", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) @parametrize def test_method_update(self, client: Anthropic) -> None: credential = client.beta.vaults.credentials.update( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) @parametrize def test_method_update_with_all_params(self, client: Anthropic) -> None: credential = client.beta.vaults.credentials.update( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", auth={ "type": "mcp_oauth", "access_token": "x", "expires_at": parse_datetime("2019-12-27T18:11:19.117Z"), "refresh": { "refresh_token": "x", "scope": "scope", "token_endpoint_auth": { "type": "client_secret_basic", "client_secret": "x", }, }, }, display_name="Example credential", metadata={"environment": "production"}, betas=["string"], ) assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) @parametrize def test_raw_response_update(self, client: Anthropic) -> None: response = client.beta.vaults.credentials.with_raw_response.update( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = response.parse() assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) @parametrize def test_streaming_response_update(self, client: Anthropic) -> None: with client.beta.vaults.credentials.with_streaming_response.update( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = response.parse() assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_update(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"): client.beta.vaults.credentials.with_raw_response.update( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `credential_id` but received ''"): client.beta.vaults.credentials.with_raw_response.update( credential_id="", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list(self, client: Anthropic) -> None: credential = client.beta.vaults.credentials.list( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert_matches_type(SyncPageCursor[BetaManagedAgentsCredential], credential, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_method_list_with_all_params(self, client: Anthropic) -> None: credential = client.beta.vaults.credentials.list( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", include_archived=True, limit=0, page="page", betas=["string"], ) assert_matches_type(SyncPageCursor[BetaManagedAgentsCredential], credential, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_raw_response_list(self, client: Anthropic) -> None: response = client.beta.vaults.credentials.with_raw_response.list( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = response.parse() assert_matches_type(SyncPageCursor[BetaManagedAgentsCredential], credential, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_streaming_response_list(self, client: Anthropic) -> None: with client.beta.vaults.credentials.with_streaming_response.list( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = response.parse() assert_matches_type(SyncPageCursor[BetaManagedAgentsCredential], credential, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize def test_path_params_list(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"): client.beta.vaults.credentials.with_raw_response.list( vault_id="", ) @parametrize def test_method_delete(self, client: Anthropic) -> None: credential = client.beta.vaults.credentials.delete( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert_matches_type(BetaManagedAgentsDeletedCredential, credential, path=["response"]) @parametrize def test_method_delete_with_all_params(self, client: Anthropic) -> None: credential = client.beta.vaults.credentials.delete( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", betas=["string"], ) assert_matches_type(BetaManagedAgentsDeletedCredential, credential, path=["response"]) @parametrize def test_raw_response_delete(self, client: Anthropic) -> None: response = client.beta.vaults.credentials.with_raw_response.delete( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = response.parse() assert_matches_type(BetaManagedAgentsDeletedCredential, credential, path=["response"]) @parametrize def test_streaming_response_delete(self, client: Anthropic) -> None: with client.beta.vaults.credentials.with_streaming_response.delete( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = response.parse() assert_matches_type(BetaManagedAgentsDeletedCredential, credential, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_delete(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"): client.beta.vaults.credentials.with_raw_response.delete( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `credential_id` but received ''"): client.beta.vaults.credentials.with_raw_response.delete( credential_id="", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) @parametrize def test_method_archive(self, client: Anthropic) -> None: credential = client.beta.vaults.credentials.archive( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) @parametrize def test_method_archive_with_all_params(self, client: Anthropic) -> None: credential = client.beta.vaults.credentials.archive( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", betas=["string"], ) assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) @parametrize def test_raw_response_archive(self, client: Anthropic) -> None: response = client.beta.vaults.credentials.with_raw_response.archive( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = response.parse() assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) @parametrize def test_streaming_response_archive(self, client: Anthropic) -> None: with client.beta.vaults.credentials.with_streaming_response.archive( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = response.parse() assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_archive(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"): client.beta.vaults.credentials.with_raw_response.archive( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `credential_id` but received ''"): client.beta.vaults.credentials.with_raw_response.archive( credential_id="", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize def test_method_mcp_oauth_validate(self, client: Anthropic) -> None: credential = client.beta.vaults.credentials.mcp_oauth_validate( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert_matches_type(BetaManagedAgentsCredentialValidation, credential, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize def test_method_mcp_oauth_validate_with_all_params(self, client: Anthropic) -> None: credential = client.beta.vaults.credentials.mcp_oauth_validate( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsCredentialValidation, credential, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize def test_raw_response_mcp_oauth_validate(self, client: Anthropic) -> None: response = client.beta.vaults.credentials.with_raw_response.mcp_oauth_validate( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = response.parse() assert_matches_type(BetaManagedAgentsCredentialValidation, credential, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize def test_streaming_response_mcp_oauth_validate(self, client: Anthropic) -> None: with client.beta.vaults.credentials.with_streaming_response.mcp_oauth_validate( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = response.parse() assert_matches_type(BetaManagedAgentsCredentialValidation, credential, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize def test_path_params_mcp_oauth_validate(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"): client.beta.vaults.credentials.with_raw_response.mcp_oauth_validate( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `credential_id` but received ''"): client.beta.vaults.credentials.with_raw_response.mcp_oauth_validate( credential_id="", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) class TestAsyncCredentials: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize async def test_method_create(self, async_client: AsyncAnthropic) -> None: credential = await async_client.beta.vaults.credentials.create( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", auth={ "token": "bearer_exampletoken", "mcp_server_url": "https://example-server.modelcontextprotocol.io/sse", "type": "static_bearer", }, ) assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) @parametrize async def test_method_create_with_all_params(self, async_client: AsyncAnthropic) -> None: credential = await async_client.beta.vaults.credentials.create( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", auth={ "token": "bearer_exampletoken", "mcp_server_url": "https://example-server.modelcontextprotocol.io/sse", "type": "static_bearer", }, display_name="Example credential", metadata={"environment": "production"}, betas=["string"], ) assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) @parametrize async def test_raw_response_create(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.vaults.credentials.with_raw_response.create( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", auth={ "token": "bearer_exampletoken", "mcp_server_url": "https://example-server.modelcontextprotocol.io/sse", "type": "static_bearer", }, ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = response.parse() assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) @parametrize async def test_streaming_response_create(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.vaults.credentials.with_streaming_response.create( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", auth={ "token": "bearer_exampletoken", "mcp_server_url": "https://example-server.modelcontextprotocol.io/sse", "type": "static_bearer", }, ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = await response.parse() assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_create(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"): await async_client.beta.vaults.credentials.with_raw_response.create( vault_id="", auth={ "token": "bearer_exampletoken", "mcp_server_url": "https://example-server.modelcontextprotocol.io/sse", "type": "static_bearer", }, ) @parametrize async def test_method_retrieve(self, async_client: AsyncAnthropic) -> None: credential = await async_client.beta.vaults.credentials.retrieve( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) @parametrize async def test_method_retrieve_with_all_params(self, async_client: AsyncAnthropic) -> None: credential = await async_client.beta.vaults.credentials.retrieve( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", betas=["string"], ) assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.vaults.credentials.with_raw_response.retrieve( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = response.parse() assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.vaults.credentials.with_streaming_response.retrieve( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = await response.parse() assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_retrieve(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"): await async_client.beta.vaults.credentials.with_raw_response.retrieve( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `credential_id` but received ''"): await async_client.beta.vaults.credentials.with_raw_response.retrieve( credential_id="", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) @parametrize async def test_method_update(self, async_client: AsyncAnthropic) -> None: credential = await async_client.beta.vaults.credentials.update( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) @parametrize async def test_method_update_with_all_params(self, async_client: AsyncAnthropic) -> None: credential = await async_client.beta.vaults.credentials.update( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", auth={ "type": "mcp_oauth", "access_token": "x", "expires_at": parse_datetime("2019-12-27T18:11:19.117Z"), "refresh": { "refresh_token": "x", "scope": "scope", "token_endpoint_auth": { "type": "client_secret_basic", "client_secret": "x", }, }, }, display_name="Example credential", metadata={"environment": "production"}, betas=["string"], ) assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) @parametrize async def test_raw_response_update(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.vaults.credentials.with_raw_response.update( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = response.parse() assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) @parametrize async def test_streaming_response_update(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.vaults.credentials.with_streaming_response.update( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = await response.parse() assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_update(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"): await async_client.beta.vaults.credentials.with_raw_response.update( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `credential_id` but received ''"): await async_client.beta.vaults.credentials.with_raw_response.update( credential_id="", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list(self, async_client: AsyncAnthropic) -> None: credential = await async_client.beta.vaults.credentials.list( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert_matches_type(AsyncPageCursor[BetaManagedAgentsCredential], credential, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_method_list_with_all_params(self, async_client: AsyncAnthropic) -> None: credential = await async_client.beta.vaults.credentials.list( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", include_archived=True, limit=0, page="page", betas=["string"], ) assert_matches_type(AsyncPageCursor[BetaManagedAgentsCredential], credential, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_raw_response_list(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.vaults.credentials.with_raw_response.list( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = response.parse() assert_matches_type(AsyncPageCursor[BetaManagedAgentsCredential], credential, path=["response"]) @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_streaming_response_list(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.vaults.credentials.with_streaming_response.list( vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = await response.parse() assert_matches_type(AsyncPageCursor[BetaManagedAgentsCredential], credential, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="buildURL drops path-level query params (SDK-4349)") @parametrize async def test_path_params_list(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"): await async_client.beta.vaults.credentials.with_raw_response.list( vault_id="", ) @parametrize async def test_method_delete(self, async_client: AsyncAnthropic) -> None: credential = await async_client.beta.vaults.credentials.delete( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert_matches_type(BetaManagedAgentsDeletedCredential, credential, path=["response"]) @parametrize async def test_method_delete_with_all_params(self, async_client: AsyncAnthropic) -> None: credential = await async_client.beta.vaults.credentials.delete( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", betas=["string"], ) assert_matches_type(BetaManagedAgentsDeletedCredential, credential, path=["response"]) @parametrize async def test_raw_response_delete(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.vaults.credentials.with_raw_response.delete( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = response.parse() assert_matches_type(BetaManagedAgentsDeletedCredential, credential, path=["response"]) @parametrize async def test_streaming_response_delete(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.vaults.credentials.with_streaming_response.delete( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = await response.parse() assert_matches_type(BetaManagedAgentsDeletedCredential, credential, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_delete(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"): await async_client.beta.vaults.credentials.with_raw_response.delete( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `credential_id` but received ''"): await async_client.beta.vaults.credentials.with_raw_response.delete( credential_id="", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) @parametrize async def test_method_archive(self, async_client: AsyncAnthropic) -> None: credential = await async_client.beta.vaults.credentials.archive( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) @parametrize async def test_method_archive_with_all_params(self, async_client: AsyncAnthropic) -> None: credential = await async_client.beta.vaults.credentials.archive( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", betas=["string"], ) assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) @parametrize async def test_raw_response_archive(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.vaults.credentials.with_raw_response.archive( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = response.parse() assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) @parametrize async def test_streaming_response_archive(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.vaults.credentials.with_streaming_response.archive( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = await response.parse() assert_matches_type(BetaManagedAgentsCredential, credential, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_archive(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"): await async_client.beta.vaults.credentials.with_raw_response.archive( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `credential_id` but received ''"): await async_client.beta.vaults.credentials.with_raw_response.archive( credential_id="", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize async def test_method_mcp_oauth_validate(self, async_client: AsyncAnthropic) -> None: credential = await async_client.beta.vaults.credentials.mcp_oauth_validate( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert_matches_type(BetaManagedAgentsCredentialValidation, credential, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize async def test_method_mcp_oauth_validate_with_all_params(self, async_client: AsyncAnthropic) -> None: credential = await async_client.beta.vaults.credentials.mcp_oauth_validate( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", betas=["message-batches-2024-09-24"], ) assert_matches_type(BetaManagedAgentsCredentialValidation, credential, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize async def test_raw_response_mcp_oauth_validate(self, async_client: AsyncAnthropic) -> None: response = await async_client.beta.vaults.credentials.with_raw_response.mcp_oauth_validate( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = response.parse() assert_matches_type(BetaManagedAgentsCredentialValidation, credential, path=["response"]) @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize async def test_streaming_response_mcp_oauth_validate(self, async_client: AsyncAnthropic) -> None: async with async_client.beta.vaults.credentials.with_streaming_response.mcp_oauth_validate( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" credential = await response.parse() assert_matches_type(BetaManagedAgentsCredentialValidation, credential, path=["response"]) assert cast(Any, response.is_closed) is True @pytest.mark.skip(reason="prism can't find endpoint with beta only tag") @parametrize async def test_path_params_mcp_oauth_validate(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `vault_id` but received ''"): await async_client.beta.vaults.credentials.with_raw_response.mcp_oauth_validate( credential_id="vcrd_011CZkZEMt8gZan2iYOQfSkw", vault_id="", ) with pytest.raises(ValueError, match=r"Expected a non-empty value for `credential_id` but received ''"): await async_client.beta.vaults.credentials.with_raw_response.mcp_oauth_validate( credential_id="", vault_id="vlt_011CZkZDLs7fYzm1hXNPeRjv", ) anthropic-sdk-python-0.120.2/tests/api_resources/messages/000077500000000000000000000000001523216435200235605ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/api_resources/messages/__init__.py000066400000000000000000000001261523216435200256700ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. anthropic-sdk-python-0.120.2/tests/api_resources/messages/test_batches.py000066400000000000000000000712061523216435200266100ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os import json from typing import Any, cast import httpx import pytest from respx import MockRouter from anthropic import Anthropic, AsyncAnthropic from tests.utils import assert_matches_type from anthropic.pagination import SyncPage, AsyncPage from anthropic.types.messages import ( MessageBatch, DeletedMessageBatch, ) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestBatches: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create(self, client: Anthropic) -> None: batch = client.messages.batches.create( requests=[ { "custom_id": "my-custom-id-1", "params": { "max_tokens": 1024, "messages": [ { "content": "Hello, world", "role": "user", } ], "model": "claude-opus-4-6", }, } ], ) assert_matches_type(MessageBatch, batch, path=["response"]) @parametrize def test_method_create_with_all_params(self, client: Anthropic) -> None: batch = client.messages.batches.create( requests=[ { "custom_id": "my-custom-id-1", "params": { "max_tokens": 1024, "messages": [ { "content": "Hello, world", "role": "user", } ], "model": "claude-opus-4-6", "cache_control": { "type": "ephemeral", "ttl": "5m", }, "container": "container", "inference_geo": "inference_geo", "metadata": {"user_id": "13803d75-b4b5-4c3e-b2a2-6f21399b021b"}, "output_config": { "effort": "low", "format": { "schema": {"foo": "bar"}, "type": "json_schema", }, }, "service_tier": "auto", "stop_sequences": ["string"], "stream": False, "system": [ { "text": "Today's date is 2024-06-01.", "type": "text", "cache_control": { "type": "ephemeral", "ttl": "5m", }, "citations": [ { "cited_text": "The grass is green. The sky is blue.", "document_index": 0, "document_title": "x", "end_char_index": 0, "start_char_index": 0, "type": "char_location", } ], } ], "temperature": 1, "thinking": { "type": "adaptive", "display": "summarized", }, "tool_choice": { "type": "auto", "disable_parallel_tool_use": True, }, "tools": [ { "input_schema": { "type": "object", "properties": { "location": "bar", "unit": "bar", }, "required": ["location"], }, "name": "name", "allowed_callers": ["direct"], "cache_control": { "type": "ephemeral", "ttl": "5m", }, "defer_loading": True, "description": "Get the current weather in a given location", "eager_input_streaming": True, "input_examples": [{"foo": "bar"}], "strict": True, "type": "custom", } ], "top_k": 5, "top_p": 0.7, }, } ], user_profile_id="anthropic-user-profile-id", ) assert_matches_type(MessageBatch, batch, path=["response"]) @parametrize def test_raw_response_create(self, client: Anthropic) -> None: response = client.messages.batches.with_raw_response.create( requests=[ { "custom_id": "my-custom-id-1", "params": { "max_tokens": 1024, "messages": [ { "content": "Hello, world", "role": "user", } ], "model": "claude-opus-4-6", }, } ], ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(MessageBatch, batch, path=["response"]) @parametrize def test_streaming_response_create(self, client: Anthropic) -> None: with client.messages.batches.with_streaming_response.create( requests=[ { "custom_id": "my-custom-id-1", "params": { "max_tokens": 1024, "messages": [ { "content": "Hello, world", "role": "user", } ], "model": "claude-opus-4-6", }, } ], ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(MessageBatch, batch, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_retrieve(self, client: Anthropic) -> None: batch = client.messages.batches.retrieve( "message_batch_id", ) assert_matches_type(MessageBatch, batch, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: Anthropic) -> None: response = client.messages.batches.with_raw_response.retrieve( "message_batch_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(MessageBatch, batch, path=["response"]) @parametrize def test_streaming_response_retrieve(self, client: Anthropic) -> None: with client.messages.batches.with_streaming_response.retrieve( "message_batch_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(MessageBatch, batch, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_retrieve(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_batch_id` but received ''"): client.messages.batches.with_raw_response.retrieve( "", ) @parametrize def test_method_list(self, client: Anthropic) -> None: batch = client.messages.batches.list() assert_matches_type(SyncPage[MessageBatch], batch, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: Anthropic) -> None: batch = client.messages.batches.list( after_id="after_id", before_id="before_id", limit=1, ) assert_matches_type(SyncPage[MessageBatch], batch, path=["response"]) @parametrize def test_raw_response_list(self, client: Anthropic) -> None: response = client.messages.batches.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(SyncPage[MessageBatch], batch, path=["response"]) @parametrize def test_streaming_response_list(self, client: Anthropic) -> None: with client.messages.batches.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(SyncPage[MessageBatch], batch, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_delete(self, client: Anthropic) -> None: batch = client.messages.batches.delete( "message_batch_id", ) assert_matches_type(DeletedMessageBatch, batch, path=["response"]) @parametrize def test_raw_response_delete(self, client: Anthropic) -> None: response = client.messages.batches.with_raw_response.delete( "message_batch_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(DeletedMessageBatch, batch, path=["response"]) @parametrize def test_streaming_response_delete(self, client: Anthropic) -> None: with client.messages.batches.with_streaming_response.delete( "message_batch_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(DeletedMessageBatch, batch, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_delete(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_batch_id` but received ''"): client.messages.batches.with_raw_response.delete( "", ) @parametrize def test_method_cancel(self, client: Anthropic) -> None: batch = client.messages.batches.cancel( "message_batch_id", ) assert_matches_type(MessageBatch, batch, path=["response"]) @parametrize def test_raw_response_cancel(self, client: Anthropic) -> None: response = client.messages.batches.with_raw_response.cancel( "message_batch_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(MessageBatch, batch, path=["response"]) @parametrize def test_streaming_response_cancel(self, client: Anthropic) -> None: with client.messages.batches.with_streaming_response.cancel( "message_batch_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(MessageBatch, batch, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_cancel(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_batch_id` but received ''"): client.messages.batches.with_raw_response.cancel( "", ) @pytest.mark.respx(base_url=base_url) @pytest.mark.parametrize("client", [False], indirect=True) def test_method_results(self, client: Anthropic, respx_mock: MockRouter) -> None: respx_mock.get("/v1/messages/batches/message_batch_id").mock( return_value=httpx.Response(200, json={"results_url": "/v1/messages/batches/message_batch_id/results"}) ) respx_mock.get("/v1/messages/batches/message_batch_id/results").mock( return_value=httpx.Response( 200, content="\n".join([json.dumps({"foo": "bar"}), json.dumps({"bar": "baz"})]) ) ) results = client.messages.batches.results( message_batch_id="message_batch_id", ) assert results.http_response is not None assert not results.http_response.is_stream_consumed i = -1 for result in results: i += 1 if i == 0: assert result.to_dict() == {"foo": "bar"} elif i == 1: assert result.to_dict() == {"bar": "baz"} else: raise RuntimeError(f"iterated too many times, expected 2 times but got {i + 1}") assert i == 1 assert results.http_response.is_stream_consumed class TestAsyncBatches: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize async def test_method_create(self, async_client: AsyncAnthropic) -> None: batch = await async_client.messages.batches.create( requests=[ { "custom_id": "my-custom-id-1", "params": { "max_tokens": 1024, "messages": [ { "content": "Hello, world", "role": "user", } ], "model": "claude-opus-4-6", }, } ], ) assert_matches_type(MessageBatch, batch, path=["response"]) @parametrize async def test_method_create_with_all_params(self, async_client: AsyncAnthropic) -> None: batch = await async_client.messages.batches.create( requests=[ { "custom_id": "my-custom-id-1", "params": { "max_tokens": 1024, "messages": [ { "content": "Hello, world", "role": "user", } ], "model": "claude-opus-4-6", "cache_control": { "type": "ephemeral", "ttl": "5m", }, "container": "container", "inference_geo": "inference_geo", "metadata": {"user_id": "13803d75-b4b5-4c3e-b2a2-6f21399b021b"}, "output_config": { "effort": "low", "format": { "schema": {"foo": "bar"}, "type": "json_schema", }, }, "service_tier": "auto", "stop_sequences": ["string"], "stream": False, "system": [ { "text": "Today's date is 2024-06-01.", "type": "text", "cache_control": { "type": "ephemeral", "ttl": "5m", }, "citations": [ { "cited_text": "The grass is green. The sky is blue.", "document_index": 0, "document_title": "x", "end_char_index": 0, "start_char_index": 0, "type": "char_location", } ], } ], "temperature": 1, "thinking": { "type": "adaptive", "display": "summarized", }, "tool_choice": { "type": "auto", "disable_parallel_tool_use": True, }, "tools": [ { "input_schema": { "type": "object", "properties": { "location": "bar", "unit": "bar", }, "required": ["location"], }, "name": "name", "allowed_callers": ["direct"], "cache_control": { "type": "ephemeral", "ttl": "5m", }, "defer_loading": True, "description": "Get the current weather in a given location", "eager_input_streaming": True, "input_examples": [{"foo": "bar"}], "strict": True, "type": "custom", } ], "top_k": 5, "top_p": 0.7, }, } ], user_profile_id="anthropic-user-profile-id", ) assert_matches_type(MessageBatch, batch, path=["response"]) @parametrize async def test_raw_response_create(self, async_client: AsyncAnthropic) -> None: response = await async_client.messages.batches.with_raw_response.create( requests=[ { "custom_id": "my-custom-id-1", "params": { "max_tokens": 1024, "messages": [ { "content": "Hello, world", "role": "user", } ], "model": "claude-opus-4-6", }, } ], ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(MessageBatch, batch, path=["response"]) @parametrize async def test_streaming_response_create(self, async_client: AsyncAnthropic) -> None: async with async_client.messages.batches.with_streaming_response.create( requests=[ { "custom_id": "my-custom-id-1", "params": { "max_tokens": 1024, "messages": [ { "content": "Hello, world", "role": "user", } ], "model": "claude-opus-4-6", }, } ], ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = await response.parse() assert_matches_type(MessageBatch, batch, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_retrieve(self, async_client: AsyncAnthropic) -> None: batch = await async_client.messages.batches.retrieve( "message_batch_id", ) assert_matches_type(MessageBatch, batch, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncAnthropic) -> None: response = await async_client.messages.batches.with_raw_response.retrieve( "message_batch_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(MessageBatch, batch, path=["response"]) @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncAnthropic) -> None: async with async_client.messages.batches.with_streaming_response.retrieve( "message_batch_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = await response.parse() assert_matches_type(MessageBatch, batch, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_retrieve(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_batch_id` but received ''"): await async_client.messages.batches.with_raw_response.retrieve( "", ) @parametrize async def test_method_list(self, async_client: AsyncAnthropic) -> None: batch = await async_client.messages.batches.list() assert_matches_type(AsyncPage[MessageBatch], batch, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncAnthropic) -> None: batch = await async_client.messages.batches.list( after_id="after_id", before_id="before_id", limit=1, ) assert_matches_type(AsyncPage[MessageBatch], batch, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncAnthropic) -> None: response = await async_client.messages.batches.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(AsyncPage[MessageBatch], batch, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncAnthropic) -> None: async with async_client.messages.batches.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = await response.parse() assert_matches_type(AsyncPage[MessageBatch], batch, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_delete(self, async_client: AsyncAnthropic) -> None: batch = await async_client.messages.batches.delete( "message_batch_id", ) assert_matches_type(DeletedMessageBatch, batch, path=["response"]) @parametrize async def test_raw_response_delete(self, async_client: AsyncAnthropic) -> None: response = await async_client.messages.batches.with_raw_response.delete( "message_batch_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(DeletedMessageBatch, batch, path=["response"]) @parametrize async def test_streaming_response_delete(self, async_client: AsyncAnthropic) -> None: async with async_client.messages.batches.with_streaming_response.delete( "message_batch_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = await response.parse() assert_matches_type(DeletedMessageBatch, batch, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_delete(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_batch_id` but received ''"): await async_client.messages.batches.with_raw_response.delete( "", ) @parametrize async def test_method_cancel(self, async_client: AsyncAnthropic) -> None: batch = await async_client.messages.batches.cancel( "message_batch_id", ) assert_matches_type(MessageBatch, batch, path=["response"]) @parametrize async def test_raw_response_cancel(self, async_client: AsyncAnthropic) -> None: response = await async_client.messages.batches.with_raw_response.cancel( "message_batch_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = response.parse() assert_matches_type(MessageBatch, batch, path=["response"]) @parametrize async def test_streaming_response_cancel(self, async_client: AsyncAnthropic) -> None: async with async_client.messages.batches.with_streaming_response.cancel( "message_batch_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" batch = await response.parse() assert_matches_type(MessageBatch, batch, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_cancel(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `message_batch_id` but received ''"): await async_client.messages.batches.with_raw_response.cancel( "", ) @pytest.mark.respx(base_url=base_url) @pytest.mark.parametrize("async_client", [False], indirect=True) async def test_method_results(self, async_client: AsyncAnthropic, respx_mock: MockRouter) -> None: respx_mock.get("/v1/messages/batches/message_batch_id").mock( return_value=httpx.Response(200, json={"results_url": "/v1/messages/batches/message_batch_id/results"}) ) respx_mock.get("/v1/messages/batches/message_batch_id/results").mock( return_value=httpx.Response( 200, content="\n".join([json.dumps({"foo": "bar"}), json.dumps({"bar": "baz"})]) ) ) results = await async_client.messages.batches.results( message_batch_id="message_batch_id", ) assert results.http_response is not None assert not results.http_response.is_stream_consumed i = -1 async for result in results: i += 1 if i == 0: assert result.to_dict() == {"foo": "bar"} elif i == 1: assert result.to_dict() == {"bar": "baz"} else: raise RuntimeError(f"iterated too many times, expected 2 times but got {i + 1}") assert i == 1 assert results.http_response.is_stream_consumed anthropic-sdk-python-0.120.2/tests/api_resources/test_completions.py000066400000000000000000000217441523216435200257260ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os from typing import Any, cast import pytest from anthropic import Anthropic, AsyncAnthropic from tests.utils import assert_matches_type from anthropic.types import Completion base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestCompletions: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create_overload_1(self, client: Anthropic) -> None: completion = client.completions.create( max_tokens_to_sample=256, model="claude-2.1", prompt="\n\nHuman: Hello, world!\n\nAssistant:", ) assert_matches_type(Completion, completion, path=["response"]) @parametrize def test_method_create_with_all_params_overload_1(self, client: Anthropic) -> None: completion = client.completions.create( max_tokens_to_sample=256, model="claude-2.1", prompt="\n\nHuman: Hello, world!\n\nAssistant:", metadata={"user_id": "13803d75-b4b5-4c3e-b2a2-6f21399b021b"}, stop_sequences=["string"], stream=False, temperature=1, top_k=5, top_p=0.7, betas=["string"], ) assert_matches_type(Completion, completion, path=["response"]) @parametrize def test_raw_response_create_overload_1(self, client: Anthropic) -> None: response = client.completions.with_raw_response.create( max_tokens_to_sample=256, model="claude-2.1", prompt="\n\nHuman: Hello, world!\n\nAssistant:", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" completion = response.parse() assert_matches_type(Completion, completion, path=["response"]) @parametrize def test_streaming_response_create_overload_1(self, client: Anthropic) -> None: with client.completions.with_streaming_response.create( max_tokens_to_sample=256, model="claude-2.1", prompt="\n\nHuman: Hello, world!\n\nAssistant:", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" completion = response.parse() assert_matches_type(Completion, completion, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_create_overload_2(self, client: Anthropic) -> None: completion_stream = client.completions.create( max_tokens_to_sample=256, model="claude-2.1", prompt="\n\nHuman: Hello, world!\n\nAssistant:", stream=True, ) completion_stream.response.close() @parametrize def test_method_create_with_all_params_overload_2(self, client: Anthropic) -> None: completion_stream = client.completions.create( max_tokens_to_sample=256, model="claude-2.1", prompt="\n\nHuman: Hello, world!\n\nAssistant:", stream=True, metadata={"user_id": "13803d75-b4b5-4c3e-b2a2-6f21399b021b"}, stop_sequences=["string"], temperature=1, top_k=5, top_p=0.7, betas=["string"], ) completion_stream.response.close() @parametrize def test_raw_response_create_overload_2(self, client: Anthropic) -> None: response = client.completions.with_raw_response.create( max_tokens_to_sample=256, model="claude-2.1", prompt="\n\nHuman: Hello, world!\n\nAssistant:", stream=True, ) assert response.http_request.headers.get("X-Stainless-Lang") == "python" stream = response.parse() stream.close() @parametrize def test_streaming_response_create_overload_2(self, client: Anthropic) -> None: with client.completions.with_streaming_response.create( max_tokens_to_sample=256, model="claude-2.1", prompt="\n\nHuman: Hello, world!\n\nAssistant:", stream=True, ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" stream = response.parse() stream.close() assert cast(Any, response.is_closed) is True class TestAsyncCompletions: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize async def test_method_create_overload_1(self, async_client: AsyncAnthropic) -> None: completion = await async_client.completions.create( max_tokens_to_sample=256, model="claude-2.1", prompt="\n\nHuman: Hello, world!\n\nAssistant:", ) assert_matches_type(Completion, completion, path=["response"]) @parametrize async def test_method_create_with_all_params_overload_1(self, async_client: AsyncAnthropic) -> None: completion = await async_client.completions.create( max_tokens_to_sample=256, model="claude-2.1", prompt="\n\nHuman: Hello, world!\n\nAssistant:", metadata={"user_id": "13803d75-b4b5-4c3e-b2a2-6f21399b021b"}, stop_sequences=["string"], stream=False, temperature=1, top_k=5, top_p=0.7, betas=["string"], ) assert_matches_type(Completion, completion, path=["response"]) @parametrize async def test_raw_response_create_overload_1(self, async_client: AsyncAnthropic) -> None: response = await async_client.completions.with_raw_response.create( max_tokens_to_sample=256, model="claude-2.1", prompt="\n\nHuman: Hello, world!\n\nAssistant:", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" completion = response.parse() assert_matches_type(Completion, completion, path=["response"]) @parametrize async def test_streaming_response_create_overload_1(self, async_client: AsyncAnthropic) -> None: async with async_client.completions.with_streaming_response.create( max_tokens_to_sample=256, model="claude-2.1", prompt="\n\nHuman: Hello, world!\n\nAssistant:", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" completion = await response.parse() assert_matches_type(Completion, completion, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_create_overload_2(self, async_client: AsyncAnthropic) -> None: completion_stream = await async_client.completions.create( max_tokens_to_sample=256, model="claude-2.1", prompt="\n\nHuman: Hello, world!\n\nAssistant:", stream=True, ) await completion_stream.response.aclose() @parametrize async def test_method_create_with_all_params_overload_2(self, async_client: AsyncAnthropic) -> None: completion_stream = await async_client.completions.create( max_tokens_to_sample=256, model="claude-2.1", prompt="\n\nHuman: Hello, world!\n\nAssistant:", stream=True, metadata={"user_id": "13803d75-b4b5-4c3e-b2a2-6f21399b021b"}, stop_sequences=["string"], temperature=1, top_k=5, top_p=0.7, betas=["string"], ) await completion_stream.response.aclose() @parametrize async def test_raw_response_create_overload_2(self, async_client: AsyncAnthropic) -> None: response = await async_client.completions.with_raw_response.create( max_tokens_to_sample=256, model="claude-2.1", prompt="\n\nHuman: Hello, world!\n\nAssistant:", stream=True, ) assert response.http_request.headers.get("X-Stainless-Lang") == "python" stream = response.parse() await stream.close() @parametrize async def test_streaming_response_create_overload_2(self, async_client: AsyncAnthropic) -> None: async with async_client.completions.with_streaming_response.create( max_tokens_to_sample=256, model="claude-2.1", prompt="\n\nHuman: Hello, world!\n\nAssistant:", stream=True, ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" stream = await response.parse() await stream.close() assert cast(Any, response.is_closed) is True anthropic-sdk-python-0.120.2/tests/api_resources/test_messages.py000066400000000000000000000724241523216435200252020ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os from typing import Any, cast import pytest from anthropic import Anthropic, AsyncAnthropic from tests.utils import assert_matches_type from anthropic.types import ( Message, MessageTokensCount, ) from anthropic.resources.messages import DEPRECATED_MODELS base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestMessages: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_create_overload_1(self, client: Anthropic) -> None: message = client.messages.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ) assert_matches_type(Message, message, path=["response"]) @parametrize def test_method_create_with_all_params_overload_1(self, client: Anthropic) -> None: message = client.messages.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", cache_control={ "type": "ephemeral", "ttl": "5m", }, container="container", inference_geo="inference_geo", metadata={"user_id": "13803d75-b4b5-4c3e-b2a2-6f21399b021b"}, output_config={ "effort": "low", "format": { "schema": {"foo": "bar"}, "type": "json_schema", }, }, service_tier="auto", stop_sequences=["string"], stream=False, system=[ { "text": "Today's date is 2024-06-01.", "type": "text", "cache_control": { "type": "ephemeral", "ttl": "5m", }, "citations": [ { "cited_text": "The grass is green. The sky is blue.", "document_index": 0, "document_title": "x", "end_char_index": 0, "start_char_index": 0, "type": "char_location", } ], } ], temperature=1, thinking={ "type": "adaptive", "display": "summarized", }, tool_choice={ "type": "auto", "disable_parallel_tool_use": True, }, tools=[ { "input_schema": { "type": "object", "properties": { "location": "bar", "unit": "bar", }, "required": ["location"], }, "name": "name", "allowed_callers": ["direct"], "cache_control": { "type": "ephemeral", "ttl": "5m", }, "defer_loading": True, "description": "Get the current weather in a given location", "eager_input_streaming": True, "input_examples": [{"foo": "bar"}], "strict": True, "type": "custom", } ], top_k=5, top_p=0.7, user_profile_id="anthropic-user-profile-id", ) assert_matches_type(Message, message, path=["response"]) @parametrize def test_raw_response_create_overload_1(self, client: Anthropic) -> None: response = client.messages.with_raw_response.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() assert_matches_type(Message, message, path=["response"]) @parametrize def test_streaming_response_create_overload_1(self, client: Anthropic) -> None: with client.messages.with_streaming_response.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() assert_matches_type(Message, message, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_method_create_overload_2(self, client: Anthropic) -> None: message_stream = client.messages.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", stream=True, ) message_stream.response.close() @parametrize def test_method_create_with_all_params_overload_2(self, client: Anthropic) -> None: message_stream = client.messages.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", stream=True, cache_control={ "type": "ephemeral", "ttl": "5m", }, container="container", inference_geo="inference_geo", metadata={"user_id": "13803d75-b4b5-4c3e-b2a2-6f21399b021b"}, output_config={ "effort": "low", "format": { "schema": {"foo": "bar"}, "type": "json_schema", }, }, service_tier="auto", stop_sequences=["string"], system=[ { "text": "Today's date is 2024-06-01.", "type": "text", "cache_control": { "type": "ephemeral", "ttl": "5m", }, "citations": [ { "cited_text": "The grass is green. The sky is blue.", "document_index": 0, "document_title": "x", "end_char_index": 0, "start_char_index": 0, "type": "char_location", } ], } ], temperature=1, thinking={ "type": "adaptive", "display": "summarized", }, tool_choice={ "type": "auto", "disable_parallel_tool_use": True, }, tools=[ { "input_schema": { "type": "object", "properties": { "location": "bar", "unit": "bar", }, "required": ["location"], }, "name": "name", "allowed_callers": ["direct"], "cache_control": { "type": "ephemeral", "ttl": "5m", }, "defer_loading": True, "description": "Get the current weather in a given location", "eager_input_streaming": True, "input_examples": [{"foo": "bar"}], "strict": True, "type": "custom", } ], top_k=5, top_p=0.7, user_profile_id="anthropic-user-profile-id", ) message_stream.response.close() @parametrize def test_raw_response_create_overload_2(self, client: Anthropic) -> None: response = client.messages.with_raw_response.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", stream=True, ) assert response.http_request.headers.get("X-Stainless-Lang") == "python" stream = response.parse() stream.close() @parametrize def test_streaming_response_create_overload_2(self, client: Anthropic) -> None: with client.messages.with_streaming_response.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", stream=True, ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" stream = response.parse() stream.close() assert cast(Any, response.is_closed) is True @parametrize def test_deprecated_model_warning(self, client: Anthropic) -> None: for deprecated_model in DEPRECATED_MODELS: with pytest.warns(DeprecationWarning, match=f"The model '{deprecated_model}' is deprecated"): client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model=deprecated_model, ) @parametrize def test_method_count_tokens(self, client: Anthropic) -> None: message = client.messages.count_tokens( messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ) assert_matches_type(MessageTokensCount, message, path=["response"]) @parametrize def test_method_count_tokens_with_all_params(self, client: Anthropic) -> None: message = client.messages.count_tokens( messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", cache_control={ "type": "ephemeral", "ttl": "5m", }, output_config={ "effort": "low", "format": { "schema": {"foo": "bar"}, "type": "json_schema", }, }, system=[ { "text": "Today's date is 2024-06-01.", "type": "text", "cache_control": { "type": "ephemeral", "ttl": "5m", }, "citations": [ { "cited_text": "The grass is green. The sky is blue.", "document_index": 0, "document_title": "x", "end_char_index": 0, "start_char_index": 0, "type": "char_location", } ], } ], thinking={ "type": "adaptive", "display": "summarized", }, tool_choice={ "type": "auto", "disable_parallel_tool_use": True, }, tools=[ { "input_schema": { "type": "object", "properties": { "location": "bar", "unit": "bar", }, "required": ["location"], }, "name": "name", "allowed_callers": ["direct"], "cache_control": { "type": "ephemeral", "ttl": "5m", }, "defer_loading": True, "description": "Get the current weather in a given location", "eager_input_streaming": True, "input_examples": [{"foo": "bar"}], "strict": True, "type": "custom", } ], user_profile_id="anthropic-user-profile-id", ) assert_matches_type(MessageTokensCount, message, path=["response"]) @parametrize def test_raw_response_count_tokens(self, client: Anthropic) -> None: response = client.messages.with_raw_response.count_tokens( messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() assert_matches_type(MessageTokensCount, message, path=["response"]) @parametrize def test_streaming_response_count_tokens(self, client: Anthropic) -> None: with client.messages.with_streaming_response.count_tokens( messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() assert_matches_type(MessageTokensCount, message, path=["response"]) assert cast(Any, response.is_closed) is True class TestAsyncMessages: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize async def test_method_create_overload_1(self, async_client: AsyncAnthropic) -> None: message = await async_client.messages.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ) assert_matches_type(Message, message, path=["response"]) @parametrize async def test_method_create_with_all_params_overload_1(self, async_client: AsyncAnthropic) -> None: message = await async_client.messages.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", cache_control={ "type": "ephemeral", "ttl": "5m", }, container="container", inference_geo="inference_geo", metadata={"user_id": "13803d75-b4b5-4c3e-b2a2-6f21399b021b"}, output_config={ "effort": "low", "format": { "schema": {"foo": "bar"}, "type": "json_schema", }, }, service_tier="auto", stop_sequences=["string"], stream=False, system=[ { "text": "Today's date is 2024-06-01.", "type": "text", "cache_control": { "type": "ephemeral", "ttl": "5m", }, "citations": [ { "cited_text": "The grass is green. The sky is blue.", "document_index": 0, "document_title": "x", "end_char_index": 0, "start_char_index": 0, "type": "char_location", } ], } ], temperature=1, thinking={ "type": "adaptive", "display": "summarized", }, tool_choice={ "type": "auto", "disable_parallel_tool_use": True, }, tools=[ { "input_schema": { "type": "object", "properties": { "location": "bar", "unit": "bar", }, "required": ["location"], }, "name": "name", "allowed_callers": ["direct"], "cache_control": { "type": "ephemeral", "ttl": "5m", }, "defer_loading": True, "description": "Get the current weather in a given location", "eager_input_streaming": True, "input_examples": [{"foo": "bar"}], "strict": True, "type": "custom", } ], top_k=5, top_p=0.7, user_profile_id="anthropic-user-profile-id", ) assert_matches_type(Message, message, path=["response"]) @parametrize async def test_raw_response_create_overload_1(self, async_client: AsyncAnthropic) -> None: response = await async_client.messages.with_raw_response.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() assert_matches_type(Message, message, path=["response"]) @parametrize async def test_streaming_response_create_overload_1(self, async_client: AsyncAnthropic) -> None: async with async_client.messages.with_streaming_response.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = await response.parse() assert_matches_type(Message, message, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_method_create_overload_2(self, async_client: AsyncAnthropic) -> None: message_stream = await async_client.messages.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", stream=True, ) await message_stream.response.aclose() @parametrize async def test_method_create_with_all_params_overload_2(self, async_client: AsyncAnthropic) -> None: message_stream = await async_client.messages.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", stream=True, cache_control={ "type": "ephemeral", "ttl": "5m", }, container="container", inference_geo="inference_geo", metadata={"user_id": "13803d75-b4b5-4c3e-b2a2-6f21399b021b"}, output_config={ "effort": "low", "format": { "schema": {"foo": "bar"}, "type": "json_schema", }, }, service_tier="auto", stop_sequences=["string"], system=[ { "text": "Today's date is 2024-06-01.", "type": "text", "cache_control": { "type": "ephemeral", "ttl": "5m", }, "citations": [ { "cited_text": "The grass is green. The sky is blue.", "document_index": 0, "document_title": "x", "end_char_index": 0, "start_char_index": 0, "type": "char_location", } ], } ], temperature=1, thinking={ "type": "adaptive", "display": "summarized", }, tool_choice={ "type": "auto", "disable_parallel_tool_use": True, }, tools=[ { "input_schema": { "type": "object", "properties": { "location": "bar", "unit": "bar", }, "required": ["location"], }, "name": "name", "allowed_callers": ["direct"], "cache_control": { "type": "ephemeral", "ttl": "5m", }, "defer_loading": True, "description": "Get the current weather in a given location", "eager_input_streaming": True, "input_examples": [{"foo": "bar"}], "strict": True, "type": "custom", } ], top_k=5, top_p=0.7, user_profile_id="anthropic-user-profile-id", ) await message_stream.response.aclose() @parametrize async def test_raw_response_create_overload_2(self, async_client: AsyncAnthropic) -> None: response = await async_client.messages.with_raw_response.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", stream=True, ) assert response.http_request.headers.get("X-Stainless-Lang") == "python" stream = response.parse() await stream.close() @parametrize async def test_streaming_response_create_overload_2(self, async_client: AsyncAnthropic) -> None: async with async_client.messages.with_streaming_response.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", stream=True, ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" stream = await response.parse() await stream.close() assert cast(Any, response.is_closed) is True @parametrize async def test_deprecated_model_warning(self, async_client: AsyncAnthropic) -> None: for deprecated_model in DEPRECATED_MODELS: with pytest.warns(DeprecationWarning, match=f"The model '{deprecated_model}' is deprecated"): await async_client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model=deprecated_model, ) @parametrize async def test_method_count_tokens(self, async_client: AsyncAnthropic) -> None: message = await async_client.messages.count_tokens( messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ) assert_matches_type(MessageTokensCount, message, path=["response"]) @parametrize async def test_method_count_tokens_with_all_params(self, async_client: AsyncAnthropic) -> None: message = await async_client.messages.count_tokens( messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", cache_control={ "type": "ephemeral", "ttl": "5m", }, output_config={ "effort": "low", "format": { "schema": {"foo": "bar"}, "type": "json_schema", }, }, system=[ { "text": "Today's date is 2024-06-01.", "type": "text", "cache_control": { "type": "ephemeral", "ttl": "5m", }, "citations": [ { "cited_text": "The grass is green. The sky is blue.", "document_index": 0, "document_title": "x", "end_char_index": 0, "start_char_index": 0, "type": "char_location", } ], } ], thinking={ "type": "adaptive", "display": "summarized", }, tool_choice={ "type": "auto", "disable_parallel_tool_use": True, }, tools=[ { "input_schema": { "type": "object", "properties": { "location": "bar", "unit": "bar", }, "required": ["location"], }, "name": "name", "allowed_callers": ["direct"], "cache_control": { "type": "ephemeral", "ttl": "5m", }, "defer_loading": True, "description": "Get the current weather in a given location", "eager_input_streaming": True, "input_examples": [{"foo": "bar"}], "strict": True, "type": "custom", } ], user_profile_id="anthropic-user-profile-id", ) assert_matches_type(MessageTokensCount, message, path=["response"]) @parametrize async def test_raw_response_count_tokens(self, async_client: AsyncAnthropic) -> None: response = await async_client.messages.with_raw_response.count_tokens( messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = response.parse() assert_matches_type(MessageTokensCount, message, path=["response"]) @parametrize async def test_streaming_response_count_tokens(self, async_client: AsyncAnthropic) -> None: async with async_client.messages.with_streaming_response.count_tokens( messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" message = await response.parse() assert_matches_type(MessageTokensCount, message, path=["response"]) assert cast(Any, response.is_closed) is True anthropic-sdk-python-0.120.2/tests/api_resources/test_models.py000066400000000000000000000157531523216435200246600ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os from typing import Any, cast import pytest from anthropic import Anthropic, AsyncAnthropic from tests.utils import assert_matches_type from anthropic.types import ModelInfo from anthropic.pagination import SyncPage, AsyncPage base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class TestModels: parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"]) @parametrize def test_method_retrieve(self, client: Anthropic) -> None: model = client.models.retrieve( model_id="model_id", ) assert_matches_type(ModelInfo, model, path=["response"]) @parametrize def test_method_retrieve_with_all_params(self, client: Anthropic) -> None: model = client.models.retrieve( model_id="model_id", betas=["string"], ) assert_matches_type(ModelInfo, model, path=["response"]) @parametrize def test_raw_response_retrieve(self, client: Anthropic) -> None: response = client.models.with_raw_response.retrieve( model_id="model_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = response.parse() assert_matches_type(ModelInfo, model, path=["response"]) @parametrize def test_streaming_response_retrieve(self, client: Anthropic) -> None: with client.models.with_streaming_response.retrieve( model_id="model_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = response.parse() assert_matches_type(ModelInfo, model, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize def test_path_params_retrieve(self, client: Anthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `model_id` but received ''"): client.models.with_raw_response.retrieve( model_id="", ) @parametrize def test_method_list(self, client: Anthropic) -> None: model = client.models.list() assert_matches_type(SyncPage[ModelInfo], model, path=["response"]) @parametrize def test_method_list_with_all_params(self, client: Anthropic) -> None: model = client.models.list( after_id="after_id", before_id="before_id", limit=1, betas=["string"], ) assert_matches_type(SyncPage[ModelInfo], model, path=["response"]) @parametrize def test_raw_response_list(self, client: Anthropic) -> None: response = client.models.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = response.parse() assert_matches_type(SyncPage[ModelInfo], model, path=["response"]) @parametrize def test_streaming_response_list(self, client: Anthropic) -> None: with client.models.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = response.parse() assert_matches_type(SyncPage[ModelInfo], model, path=["response"]) assert cast(Any, response.is_closed) is True class TestAsyncModels: parametrize = pytest.mark.parametrize( "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"] ) @parametrize async def test_method_retrieve(self, async_client: AsyncAnthropic) -> None: model = await async_client.models.retrieve( model_id="model_id", ) assert_matches_type(ModelInfo, model, path=["response"]) @parametrize async def test_method_retrieve_with_all_params(self, async_client: AsyncAnthropic) -> None: model = await async_client.models.retrieve( model_id="model_id", betas=["string"], ) assert_matches_type(ModelInfo, model, path=["response"]) @parametrize async def test_raw_response_retrieve(self, async_client: AsyncAnthropic) -> None: response = await async_client.models.with_raw_response.retrieve( model_id="model_id", ) assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = response.parse() assert_matches_type(ModelInfo, model, path=["response"]) @parametrize async def test_streaming_response_retrieve(self, async_client: AsyncAnthropic) -> None: async with async_client.models.with_streaming_response.retrieve( model_id="model_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = await response.parse() assert_matches_type(ModelInfo, model, path=["response"]) assert cast(Any, response.is_closed) is True @parametrize async def test_path_params_retrieve(self, async_client: AsyncAnthropic) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `model_id` but received ''"): await async_client.models.with_raw_response.retrieve( model_id="", ) @parametrize async def test_method_list(self, async_client: AsyncAnthropic) -> None: model = await async_client.models.list() assert_matches_type(AsyncPage[ModelInfo], model, path=["response"]) @parametrize async def test_method_list_with_all_params(self, async_client: AsyncAnthropic) -> None: model = await async_client.models.list( after_id="after_id", before_id="before_id", limit=1, betas=["string"], ) assert_matches_type(AsyncPage[ModelInfo], model, path=["response"]) @parametrize async def test_raw_response_list(self, async_client: AsyncAnthropic) -> None: response = await async_client.models.with_raw_response.list() assert response.is_closed is True assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = response.parse() assert_matches_type(AsyncPage[ModelInfo], model, path=["response"]) @parametrize async def test_streaming_response_list(self, async_client: AsyncAnthropic) -> None: async with async_client.models.with_streaming_response.list() as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" model = await response.parse() assert_matches_type(AsyncPage[ModelInfo], model, path=["response"]) assert cast(Any, response.is_closed) is True anthropic-sdk-python-0.120.2/tests/conftest.py000066400000000000000000000111221523216435200213020ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import os import logging from typing import TYPE_CHECKING, Any, Iterator, AsyncIterator import httpx import pytest import inline_snapshot from http_snapshot import SnapshotSerializerOptions from pytest_asyncio import is_async_test from http_snapshot.httpx import HttpxSyncSnapshotClient, HttpxAsyncSnapshotClient from anthropic import Anthropic, AsyncAnthropic, DefaultAioHttpClient from anthropic._utils import is_dict if TYPE_CHECKING: from _pytest.fixtures import FixtureRequest # pyright: ignore[reportPrivateImportUsage] pytest.register_assert_rewrite("tests.utils") logging.getLogger("anthropic").setLevel(logging.DEBUG) SNAPSHOT_RESPONSE_HEADERS_EXCLUDE = [ "date", "request-id", "anthropic-organization-id", "x-envoy-upstream-service-time", "cf-ray", ] SNAPSHOT_REQUEST_HEADERS_EXCLUDE = [ "x-api-key", ] # automatically add `pytest.mark.asyncio()` to all of our async tests # so we don't have to add that boilerplate everywhere def pytest_collection_modifyitems(items: list[pytest.Function]) -> None: pytest_asyncio_tests = (item for item in items if is_async_test(item)) session_scope_marker = pytest.mark.asyncio(loop_scope="session") for async_test in pytest_asyncio_tests: async_test.add_marker(session_scope_marker, append=False) # We skip tests that use both the aiohttp client and respx_mock as respx_mock # doesn't support custom transports. for item in items: if "async_client" not in item.fixturenames or "respx_mock" not in item.fixturenames: continue if not hasattr(item, "callspec"): continue async_client_param = item.callspec.params.get("async_client") if is_dict(async_client_param) and async_client_param.get("http_client") == "aiohttp": item.add_marker(pytest.mark.skip(reason="aiohttp client is not compatible with respx_mock")) base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") api_key = "my-anthropic-api-key" @pytest.fixture(scope="session") def client(request: FixtureRequest) -> Iterator[Anthropic]: strict = getattr(request, "param", True) if not isinstance(strict, bool): raise TypeError(f"Unexpected fixture parameter type {type(strict)}, expected {bool}") with Anthropic(base_url=base_url, api_key=api_key, _strict_response_validation=strict) as client: yield client @pytest.fixture def http_snapshot_serializer_options() -> SnapshotSerializerOptions: return SnapshotSerializerOptions( exclude_response_headers=SNAPSHOT_RESPONSE_HEADERS_EXCLUDE, exclude_request_headers=SNAPSHOT_REQUEST_HEADERS_EXCLUDE, include_request=True, ) @pytest.fixture(scope="function") def snapshot_client( is_recording: bool, http_snapshot_serializer_options: SnapshotSerializerOptions, http_snapshot: inline_snapshot.Snapshot[Any], ) -> Iterator[Anthropic]: with HttpxSyncSnapshotClient( http_snapshot, is_recording, serializer_options=http_snapshot_serializer_options ) as snapshot_client: with Anthropic(http_client=snapshot_client, api_key=None if is_recording else api_key) as client: yield client @pytest.fixture(scope="function") async def async_snapshot_client( is_recording: bool, http_snapshot_serializer_options: SnapshotSerializerOptions, http_snapshot: inline_snapshot.Snapshot[Any], ) -> AsyncIterator[AsyncAnthropic]: async with HttpxAsyncSnapshotClient( http_snapshot, is_recording, serializer_options=http_snapshot_serializer_options ) as snapshot_client: client = AsyncAnthropic(http_client=snapshot_client, api_key=None if is_recording else api_key) yield client @pytest.fixture(scope="session") async def async_client(request: FixtureRequest) -> AsyncIterator[AsyncAnthropic]: param = getattr(request, "param", True) # defaults strict = True http_client: None | httpx.AsyncClient = None if isinstance(param, bool): strict = param elif is_dict(param): strict = param.get("strict", True) assert isinstance(strict, bool) http_client_type = param.get("http_client", "httpx") if http_client_type == "aiohttp": http_client = DefaultAioHttpClient() else: raise TypeError(f"Unexpected fixture parameter type {type(param)}, expected bool or dict") async with AsyncAnthropic( base_url=base_url, api_key=api_key, _strict_response_validation=strict, http_client=http_client ) as client: yield client anthropic-sdk-python-0.120.2/tests/decoders/000077500000000000000000000000001523216435200206765ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/decoders/test_jsonl.py000066400000000000000000000051401523216435200234340ustar00rootroot00000000000000from __future__ import annotations from typing import Any, Iterator, AsyncIterator from typing_extensions import TypeVar import httpx import pytest from anthropic._decoders.jsonl import JSONLDecoder, AsyncJSONLDecoder _T = TypeVar("_T") @pytest.mark.asyncio @pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) async def test_basic(sync: bool) -> None: def body() -> Iterator[bytes]: yield b'{"foo":true}\n' yield b'{"bar":false}\n' iterator = make_jsonl_iterator( content=body(), sync=sync, line_type=object, ) assert await iter_next(iterator) == {"foo": True} assert await iter_next(iterator) == {"bar": False} await assert_empty_iter(iterator) @pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) async def test_new_lines_in_json( sync: bool, ) -> None: def body() -> Iterator[bytes]: yield b'{"content":"Hello, world!\\nHow are you doing?"}' iterator = make_jsonl_iterator(content=body(), sync=sync, line_type=object) assert await iter_next(iterator) == {"content": "Hello, world!\nHow are you doing?"} @pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) async def test_multi_byte_character_multiple_chunks( sync: bool, ) -> None: def body() -> Iterator[bytes]: yield b'{"content":"' # bytes taken from the string 'извеÑтни' and arbitrarily split # so that some multi-byte characters span multiple chunks yield b"\xd0" yield b"\xb8\xd0\xb7\xd0" yield b"\xb2\xd0\xb5\xd1\x81\xd1\x82\xd0\xbd\xd0\xb8" yield b'"}\n' iterator = make_jsonl_iterator(content=body(), sync=sync, line_type=object) assert await iter_next(iterator) == {"content": "извеÑтни"} async def to_aiter(iter: Iterator[bytes]) -> AsyncIterator[bytes]: for chunk in iter: yield chunk async def iter_next(iter: Iterator[_T] | AsyncIterator[_T]) -> _T: if isinstance(iter, AsyncIterator): return await iter.__anext__() return next(iter) async def assert_empty_iter(decoder: JSONLDecoder[Any] | AsyncJSONLDecoder[Any]) -> None: with pytest.raises((StopAsyncIteration, RuntimeError)): await iter_next(decoder) def make_jsonl_iterator( content: Iterator[bytes], *, sync: bool, line_type: type[_T], ) -> JSONLDecoder[_T] | AsyncJSONLDecoder[_T]: if sync: return JSONLDecoder(line_type=line_type, raw_iterator=content, http_response=httpx.Response(200)) return AsyncJSONLDecoder(line_type=line_type, raw_iterator=to_aiter(content), http_response=httpx.Response(200)) anthropic-sdk-python-0.120.2/tests/fixtures/000077500000000000000000000000001523216435200207575ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/fixtures/fable-fallback/000077500000000000000000000000001523216435200235655ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/fixtures/fable-fallback/stream-a-refusal.sse000066400000000000000000000051731523216435200274570ustar00rootroot00000000000000event: message_start data: {"type":"message_start","message":{"model":"claude-fable-5","id":"msg_fixture_a_0001","type":"message","role":"assistant","content":[],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":28,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":5,"service_tier":"standard","inference_geo":"global"}} } event: content_block_start data: {"type":"content_block_start","index":0,"content_block":{"type":"thinking","thinking":"","signature":""} } event: ping data: {"type": "ping"} event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":"Simple educ"} } event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":"ational question about what a solar eclipse is. This is benign general knowledge — definitions are fine. Also the user called"} } event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":" me \"claudius\" — I'm Claude. Minor correction or just roll with it politely."} } event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":""} } event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"signature_delta","signature":"c3ludGhldGljLXNpZ25hdHVyZS1maXh0dXJlLWEtbm90LWEtcmVhbC1zaWduYXR1cmU="} } event: content_block_stop data: {"type":"content_block_stop","index":0 } event: content_block_start data: {"type":"content_block_start","index":1,"content_block":{"type":"text","text":""}} event: content_block_delta data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"Hi"} } event: content_block_stop data: {"type":"content_block_stop","index":1 } event: message_delta data: {"type":"message_delta","delta":{"stop_reason":"refusal","stop_sequence":null,"stop_details":{"type":"refusal","category":null,"explanation":null,"fallback_credit_token":"tok_synthetic_fixture_a","fallback_has_prefill_claim":true}},"usage":{"input_tokens":28,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":106,"output_tokens_details":{"thinking_tokens":67},"iterations":[{"input_tokens":28,"output_tokens":106,"cache_read_input_tokens":0,"cache_creation_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"type":"message"}]} } event: message_stop data: {"type":"message_stop" } anthropic-sdk-python-0.120.2/tests/fixtures/fable-fallback/stream-a-toolrefusal.sse000066400000000000000000000107331523216435200303530ustar00rootroot00000000000000event: message_start data: {"type":"message_start","message":{"model":"claude-fable-5","id":"msg_fixture_atool_0001","type":"message","role":"assistant","content":[],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":612,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":63,"service_tier":"standard","inference_geo":"global"}} } event: content_block_start data: {"type":"content_block_start","index":0,"content_block":{"type":"server_tool_use","id":"srvtoolu_fixture_a_0001","name":"web_search","input":{}} } event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":""} } event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"{\"query\": \"s"} } event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"olar eclips"} } event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"e viewing s"} } event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"af"} } event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"ety news"} } event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":" 2026"}} event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"\"}"} } event: content_block_stop data: {"type":"content_block_stop","index":0 } event: content_block_start data: {"type":"content_block_start","index":1,"content_block":{"type":"web_search_tool_result","tool_use_id":"srvtoolu_fixture_a_0001","content":[{"type":"web_search_result","title":"How to Watch a Solar Eclipse Safely | Example Observatory","url":"https://www.example.com/eclipse-viewing-safety","encrypted_content":"Looking directly at the Sun without certified eye protection is never safe, even during a partial eclipse. Certified eclipse glasses block enough light to make direct viewing safe; ordinary sunglasses do not. A pinhole projector is an easy, safe alternative that projects an image of the Sun onto a flat surface. During the brief window of totality — and only then — it is safe to look with the naked eye, and observers can see the Sun's corona.","page_age":"April 30, 2026"},{"type":"web_search_result","title":"Upcoming Solar Eclipses in 2026 | Example Astronomy News","url":"https://www.example.com/eclipses-2026","encrypted_content":"Skywatchers have two solar eclipses to look forward to in 2026. An annular eclipse in February will be visible from parts of the southern hemisphere, and a total eclipse in August will trace a narrow path of totality across parts of the Arctic and western Europe. Outside the path of totality, observers across a much wider region will see a partial eclipse. Local astronomy clubs are organising public viewing events with certified equipment along the route.","page_age":"1 week ago"}],"caller":{"type":"direct"}} } event: content_block_stop data: {"type":"content_block_stop","index":1 } event: content_block_start data: {"type":"content_block_start","index":2,"content_block":{"type":"text","text":""} } event: content_block_delta data: {"type":"content_block_delta","index":2,"delta":{"type":"text_delta","text":"Here's a summary of this year's solar eclipses and how"} } event: content_block_stop data: {"type":"content_block_stop","index":2} event: message_delta data: {"type":"message_delta","delta":{"stop_reason":"refusal","stop_sequence":null,"stop_details":{"type":"refusal","category":null,"explanation":null,"fallback_credit_token":"tok_synthetic_fixture_a","fallback_has_prefill_claim":true}},"usage":{"input_tokens":28,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":106,"output_tokens_details":{"thinking_tokens":67},"iterations":[{"input_tokens":28,"output_tokens":106,"cache_read_input_tokens":0,"cache_creation_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"type":"message"}]} } event: message_stop data: {"type":"message_stop" } anthropic-sdk-python-0.120.2/tests/fixtures/fable-fallback/stream-b-fallback.sse000066400000000000000000000105711523216435200275540ustar00rootroot00000000000000event: message_start data: {"type":"message_start","message":{"model":"claude-opus-4-8","id":"msg_fixture_b_0001","type":"message","role":"assistant","content":[],"stop_reason":null,"stop_sequence":null,"stop_details":null,"usage":{"input_tokens":31,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":7,"service_tier":"standard","inference_geo":"global"}} } event: content_block_start data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""} } event: ping data: {"type": "ping"} event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" there! Happy to expl"} } event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"ain.\n\n**Solar eclipses** happen when the Moon passes between the Sun and the Earth, casting a shadow that blocks s"} } event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"ome or all of the Sun's light for viewers inside it. They're one of the most dramatic sights in the sky."} } event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"\n\nSome key points:\n\n**The three main types:**\n- Total"} } event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" (the Moon completely covers the Sun's disk)\n- Partial (only part of the Sun is covered)\n- Annular (the"} } event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" famous *ring of fire*)\n\n**Why they're so striking:**\n- Totality can turn day into twilight (for"} } event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" a few minutes), and the Sun's corona becomes visible to the naked eye\n- Temperatures can drop noticeably while the shadow"} } event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" passes over\n- Animals sometimes behave as if night has fallen\n\n**Viewing safely:**"} } event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"\n- **Certified eclipse glasses** are a must for looking at the Sun directly. Ordinary"} } event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" sunglasses are not safe, even very dark ones.\n- A pinhole projector is an easy, safe alter"} } event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"native.\n\n**Why they're rare:**\nThe Moon's shadow traces a ("} } event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"narrow) path across the Earth, so any given spot sees a total eclipse only"} } event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" about once every few centuries. That rarity is a big part of the excitement.\n\nIs there a particular angle you're cur"} } event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"ious about? For example, I'd be happy to go deeper on the history, the science of totality, safe viewing, or how they"}} event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"'re portrayed in fiction. Just let me know what's sparking your interest!"} } event: content_block_stop data: {"type":"content_block_stop","index":0 } event: message_delta data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null,"stop_details":null},"usage":{"input_tokens":31,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":547,"output_tokens_details":{"thinking_tokens":0},"iterations":[{"input_tokens":31,"output_tokens":547,"cache_read_input_tokens":0,"cache_creation_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"type":"message"}]} } event: message_stop data: {"type":"message_stop" } anthropic-sdk-python-0.120.2/tests/lib/000077500000000000000000000000001523216435200176545ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/__init__.py000066400000000000000000000000001523216435200217530ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/_parse/000077500000000000000000000000001523216435200211255ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/_parse/__init__.py000066400000000000000000000000001523216435200232240ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/_parse/__inline_snapshot__/000077500000000000000000000000001523216435200251165ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/_parse/__inline_snapshot__/test_beta_messages/000077500000000000000000000000001523216435200307575ustar00rootroot00000000000000TestAsyncMessages/000077500000000000000000000000001523216435200343055ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/_parse/__inline_snapshot__/test_beta_messages9381e2b7-7fa1-46f7-9f78-46dc8431ea9d.json000066400000000000000000000073601523216435200420260ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/_parse/__inline_snapshot__/test_beta_messages/TestAsyncMessages[ { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages?beta=true", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "NOT_GIVEN", "accept": "application/json", "content-type": "application/json", "user-agent": "AsyncAnthropic/Python 0.82.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.82.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.9.18", "x-stainless-async": "async:asyncio", "anthropic-version": "2023-06-01", "x-stainless-helper-method": "stream", "x-stainless-stream-helper": "beta.messages", "anthropic-beta": "structured-outputs-2025-12-15", "x-stainless-retry-count": "0", "x-stainless-read-timeout": "600", "content-length": "276" }, "body": { "max_tokens": 1024, "messages": [ { "role": "user", "content": "Extract order IDs from the following text:\n\nOrder 12345\nOrder 67890" } ], "model": "claude-sonnet-4-5", "output_config": { "format": { "type": "json_schema", "schema": { "type": "array", "items": { "type": "integer" } } } }, "stream": true } }, "response": { "status_code": 200, "headers": { "content-type": "text/event-stream; charset=utf-8", "connection": "keep-alive", "cache-control": "no-cache", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "x-robots-tag": "none", "content-security-policy": "default-src 'none'; frame-ancestors 'none'", "cf-cache-status": "DYNAMIC" }, "body": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-sonnet-4-5-20250929\",\"id\":\"msg_01LtFpmR8SbmiK2kRA5sWXNi\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":135,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":1,\"service_tier\":\"standard\",\"inference_geo\":\"not_available\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"[\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"12\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"345,\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"67890]\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":135,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":10} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n" } } ]d7092a7d-b723-4470-8fb0-da138cd103a1.json000066400000000000000000000061331523216435200417430ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/_parse/__inline_snapshot__/test_beta_messages/TestAsyncMessages[ { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages?beta=true", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "600", "accept": "application/json", "content-type": "application/json", "user-agent": "AsyncAnthropic/Python 0.82.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.82.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.9.18", "x-stainless-async": "async:asyncio", "anthropic-version": "2023-06-01", "x-stainless-helper": "beta.messages.parse", "anthropic-beta": "structured-outputs-2025-12-15", "x-stainless-raw-response": "true", "x-stainless-retry-count": "0", "x-stainless-read-timeout": "600", "content-length": "432" }, "body": { "max_tokens": 1024, "messages": [ { "role": "user", "content": "Extract the user's name and age from the following text:\n\nMy name is John Doe and I am 30 years old." } ], "model": "claude-sonnet-4-5", "output_config": { "format": { "schema": { "type": "object", "title": "User", "properties": { "name": { "type": "string", "title": "Name" }, "age": { "type": "integer", "title": "Age" } }, "additionalProperties": false, "required": [ "name", "age" ] }, "type": "json_schema" } } } }, "response": { "status_code": 200, "headers": { "content-type": "application/json", "connection": "keep-alive", "x-robots-tag": "none", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "content-security-policy": "default-src 'none'; frame-ancestors 'none'", "cf-cache-status": "DYNAMIC" }, "body": { "model": "claude-sonnet-4-5-20250929", "id": "msg_01EojSKby3oqoP7mb4PHsMJ7", "type": "message", "role": "assistant", "content": [ { "type": "text", "text": "{\"name\":\"John Doe\",\"age\":30}" } ], "stop_reason": "end_turn", "stop_sequence": null, "usage": { "input_tokens": 222, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": { "ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0 }, "output_tokens": 14, "service_tier": "standard", "inference_geo": "not_available" } } } } ]anthropic-sdk-python-0.120.2/tests/lib/_parse/__inline_snapshot__/test_messages/000077500000000000000000000000001523216435200277645ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/_parse/__inline_snapshot__/test_messages/TestAsyncParse/000077500000000000000000000000001523216435200326745ustar00rootroot00000000000000924cfccc-c863-44ed-9838-c36b37416eaf.json000066400000000000000000000062141523216435200404570ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/_parse/__inline_snapshot__/test_messages/TestAsyncParse[ { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "600", "accept": "application/json", "content-type": "application/json", "user-agent": "AsyncAnthropic/Python 0.82.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.82.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.9.18", "x-stainless-async": "async:asyncio", "anthropic-version": "2023-06-01", "x-stainless-helper": "messages.parse", "x-stainless-retry-count": "0", "x-stainless-read-timeout": "600", "content-length": "474" }, "body": { "max_tokens": 1024, "messages": [ { "role": "user", "content": "Extract: I want to order 2 Green Tea at $5.50 each" } ], "model": "claude-sonnet-4-5", "output_config": { "format": { "schema": { "type": "object", "title": "OrderItem", "properties": { "product_name": { "type": "string", "title": "Product Name" }, "price": { "type": "number", "title": "Price" }, "quantity": { "type": "integer", "title": "Quantity" } }, "additionalProperties": false, "required": [ "product_name", "price", "quantity" ] }, "type": "json_schema" } } } }, "response": { "status_code": 200, "headers": { "content-type": "application/json", "connection": "keep-alive", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "cf-cache-status": "DYNAMIC", "x-robots-tag": "none", "content-security-policy": "default-src 'none'; frame-ancestors 'none'" }, "body": { "model": "claude-sonnet-4-5-20250929", "id": "msg_01MkoP42QB7TUf5m2NVVK2kE", "type": "message", "role": "assistant", "content": [ { "type": "text", "text": "{\"product_name\": \"Green Tea\", \"price\": 5.50, \"quantity\": 2}" } ], "stop_reason": "end_turn", "stop_sequence": null, "usage": { "input_tokens": 249, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": { "ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0 }, "output_tokens": 26, "service_tier": "standard", "inference_geo": "not_available" } } } } ]d743d628-16aa-4c9f-ae78-51deb578f746.json000066400000000000000000000273221523216435200404070ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/_parse/__inline_snapshot__/test_messages/TestAsyncParse[ { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "600", "accept": "application/json", "content-type": "application/json", "user-agent": "AsyncAnthropic/Python 0.82.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.82.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.9.18", "x-stainless-async": "async:asyncio", "anthropic-version": "2023-06-01", "x-stainless-helper": "messages.parse", "x-stainless-retry-count": "0", "x-stainless-read-timeout": "600", "content-length": "749" }, "body": { "max_tokens": 1024, "messages": [ { "role": "user", "content": "Extract order: 2 Green Tea at $5.50 and 1 Coffee at $3.00. Total $14." } ], "model": "claude-sonnet-4-5", "output_config": { "format": { "schema": { "$defs": { "OrderItem": { "type": "object", "title": "OrderItem", "properties": { "product_name": { "type": "string", "title": "Product Name" }, "price": { "type": "number", "title": "Price" }, "quantity": { "type": "integer", "title": "Quantity" } }, "additionalProperties": false, "required": [ "product_name", "price", "quantity" ] } }, "type": "object", "title": "OrderDetails", "properties": { "items": { "type": "array", "title": "Items", "items": { "$ref": "#/$defs/OrderItem" } }, "total": { "type": "number", "title": "Total" } }, "additionalProperties": false, "required": [ "items", "total" ] }, "type": "json_schema" } } } }, "response": { "status_code": 429, "headers": { "content-type": "application/json", "content-length": "606", "connection": "keep-alive", "x-should-retry": "true", "x-robots-tag": "none", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "content-security-policy": "default-src 'none'; frame-ancestors 'none'", "cf-cache-status": "DYNAMIC" }, "body": { "type": "error", "error": { "type": "rate_limit_error", "message": "This request would exceed your organization's rate limit of 20,000,000 prompt bytes per hour (org: 5576611f-a1ee-427c-9800-298d9579899c, model: claude-sonnet-4-5-20250929). For details, refer to: https://docs.claude.com/en/api/rate-limits. You can see the response headers for current usage. Please reduce the prompt length or the maximum tokens requested, or try again later. You may also contact sales at https://www.anthropic.com/contact-sales to discuss your options for a rate limit increase." }, "request_id": "req_011CYK5mje9HkutJtmfPzzNC" } } }, { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "600", "accept": "application/json", "content-type": "application/json", "user-agent": "AsyncAnthropic/Python 0.82.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.82.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.9.18", "x-stainless-async": "async:asyncio", "anthropic-version": "2023-06-01", "x-stainless-helper": "messages.parse", "x-stainless-retry-count": "1", "x-stainless-read-timeout": "600", "content-length": "749" }, "body": { "max_tokens": 1024, "messages": [ { "role": "user", "content": "Extract order: 2 Green Tea at $5.50 and 1 Coffee at $3.00. Total $14." } ], "model": "claude-sonnet-4-5", "output_config": { "format": { "schema": { "$defs": { "OrderItem": { "type": "object", "title": "OrderItem", "properties": { "product_name": { "type": "string", "title": "Product Name" }, "price": { "type": "number", "title": "Price" }, "quantity": { "type": "integer", "title": "Quantity" } }, "additionalProperties": false, "required": [ "product_name", "price", "quantity" ] } }, "type": "object", "title": "OrderDetails", "properties": { "items": { "type": "array", "title": "Items", "items": { "$ref": "#/$defs/OrderItem" } }, "total": { "type": "number", "title": "Total" } }, "additionalProperties": false, "required": [ "items", "total" ] }, "type": "json_schema" } } } }, "response": { "status_code": 429, "headers": { "content-type": "application/json", "content-length": "606", "connection": "keep-alive", "x-should-retry": "true", "x-robots-tag": "none", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "content-security-policy": "default-src 'none'; frame-ancestors 'none'", "cf-cache-status": "DYNAMIC" }, "body": { "type": "error", "error": { "type": "rate_limit_error", "message": "This request would exceed your organization's rate limit of 20,000,000 prompt bytes per hour (org: 5576611f-a1ee-427c-9800-298d9579899c, model: claude-sonnet-4-5-20250929). For details, refer to: https://docs.claude.com/en/api/rate-limits. You can see the response headers for current usage. Please reduce the prompt length or the maximum tokens requested, or try again later. You may also contact sales at https://www.anthropic.com/contact-sales to discuss your options for a rate limit increase." }, "request_id": "req_011CYK5mnscLpxFuMxiDHt26" } } }, { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "600", "accept": "application/json", "content-type": "application/json", "user-agent": "AsyncAnthropic/Python 0.82.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.82.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.9.18", "x-stainless-async": "async:asyncio", "anthropic-version": "2023-06-01", "x-stainless-helper": "messages.parse", "x-stainless-retry-count": "2", "x-stainless-read-timeout": "600", "content-length": "749" }, "body": { "max_tokens": 1024, "messages": [ { "role": "user", "content": "Extract order: 2 Green Tea at $5.50 and 1 Coffee at $3.00. Total $14." } ], "model": "claude-sonnet-4-5", "output_config": { "format": { "schema": { "$defs": { "OrderItem": { "type": "object", "title": "OrderItem", "properties": { "product_name": { "type": "string", "title": "Product Name" }, "price": { "type": "number", "title": "Price" }, "quantity": { "type": "integer", "title": "Quantity" } }, "additionalProperties": false, "required": [ "product_name", "price", "quantity" ] } }, "type": "object", "title": "OrderDetails", "properties": { "items": { "type": "array", "title": "Items", "items": { "$ref": "#/$defs/OrderItem" } }, "total": { "type": "number", "title": "Total" } }, "additionalProperties": false, "required": [ "items", "total" ] }, "type": "json_schema" } } } }, "response": { "status_code": 200, "headers": { "content-type": "application/json", "connection": "keep-alive", "x-robots-tag": "none", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "content-security-policy": "default-src 'none'; frame-ancestors 'none'", "cf-cache-status": "DYNAMIC" }, "body": { "model": "claude-sonnet-4-5-20250929", "id": "msg_01Q1NNiF8NA8EywFhxTmFbeP", "type": "message", "role": "assistant", "content": [ { "type": "text", "text": "{\"items\":[{\"product_name\":\"Green Tea\",\"price\":5.50,\"quantity\":2},{\"product_name\":\"Coffee\",\"price\":3.00,\"quantity\":1}],\"total\":14.0}" } ], "stop_reason": "end_turn", "stop_sequence": null, "usage": { "input_tokens": 406, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": { "ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0 }, "output_tokens": 50, "service_tier": "standard", "inference_geo": "not_available" } } } } ]anthropic-sdk-python-0.120.2/tests/lib/_parse/__inline_snapshot__/test_messages/TestAsyncStream/000077500000000000000000000000001523216435200330555ustar00rootroot00000000000000a8b7dbb8-2321-4624-abdb-7ba2136b5c38.json000066400000000000000000000217221523216435200405760ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/_parse/__inline_snapshot__/test_messages/TestAsyncStream[ { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "NOT_GIVEN", "accept": "application/json", "content-type": "application/json", "user-agent": "AsyncAnthropic/Python 0.82.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.82.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.9.18", "x-stainless-async": "async:asyncio", "anthropic-version": "2023-06-01", "x-stainless-helper-method": "stream", "x-stainless-stream-helper": "messages", "x-stainless-retry-count": "0", "x-stainless-read-timeout": "600", "content-length": "276" }, "body": { "max_tokens": 1024, "messages": [ { "role": "user", "content": "Extract order IDs from the following text:\n\nOrder 12345\nOrder 67890" } ], "model": "claude-sonnet-4-5", "output_config": { "format": { "type": "json_schema", "schema": { "type": "array", "items": { "type": "integer" } } } }, "stream": true } }, "response": { "status_code": 429, "headers": { "content-type": "application/json", "content-length": "606", "connection": "keep-alive", "x-should-retry": "true", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "cf-cache-status": "DYNAMIC", "x-robots-tag": "none", "content-security-policy": "default-src 'none'; frame-ancestors 'none'" }, "body": { "type": "error", "error": { "type": "rate_limit_error", "message": "This request would exceed your organization's rate limit of 20,000,000 prompt bytes per hour (org: 5576611f-a1ee-427c-9800-298d9579899c, model: claude-sonnet-4-5-20250929). For details, refer to: https://docs.claude.com/en/api/rate-limits. You can see the response headers for current usage. Please reduce the prompt length or the maximum tokens requested, or try again later. You may also contact sales at https://www.anthropic.com/contact-sales to discuss your options for a rate limit increase." }, "request_id": "req_011CYK5n67SytMUxm8WMZUWY" } } }, { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "NOT_GIVEN", "accept": "application/json", "content-type": "application/json", "user-agent": "AsyncAnthropic/Python 0.82.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.82.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.9.18", "x-stainless-async": "async:asyncio", "anthropic-version": "2023-06-01", "x-stainless-helper-method": "stream", "x-stainless-stream-helper": "messages", "x-stainless-retry-count": "1", "x-stainless-read-timeout": "600", "content-length": "276" }, "body": { "max_tokens": 1024, "messages": [ { "role": "user", "content": "Extract order IDs from the following text:\n\nOrder 12345\nOrder 67890" } ], "model": "claude-sonnet-4-5", "output_config": { "format": { "type": "json_schema", "schema": { "type": "array", "items": { "type": "integer" } } } }, "stream": true } }, "response": { "status_code": 429, "headers": { "content-type": "application/json", "content-length": "606", "connection": "keep-alive", "x-should-retry": "true", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "cf-cache-status": "DYNAMIC", "x-robots-tag": "none", "content-security-policy": "default-src 'none'; frame-ancestors 'none'" }, "body": { "type": "error", "error": { "type": "rate_limit_error", "message": "This request would exceed your organization's rate limit of 20,000,000 prompt bytes per hour (org: 5576611f-a1ee-427c-9800-298d9579899c, model: claude-sonnet-4-5-20250929). For details, refer to: https://docs.claude.com/en/api/rate-limits. You can see the response headers for current usage. Please reduce the prompt length or the maximum tokens requested, or try again later. You may also contact sales at https://www.anthropic.com/contact-sales to discuss your options for a rate limit increase." }, "request_id": "req_011CYK5n94JW2KDxvEjTDLeR" } } }, { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "NOT_GIVEN", "accept": "application/json", "content-type": "application/json", "user-agent": "AsyncAnthropic/Python 0.82.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.82.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.9.18", "x-stainless-async": "async:asyncio", "anthropic-version": "2023-06-01", "x-stainless-helper-method": "stream", "x-stainless-stream-helper": "messages", "x-stainless-retry-count": "2", "x-stainless-read-timeout": "600", "content-length": "276" }, "body": { "max_tokens": 1024, "messages": [ { "role": "user", "content": "Extract order IDs from the following text:\n\nOrder 12345\nOrder 67890" } ], "model": "claude-sonnet-4-5", "output_config": { "format": { "type": "json_schema", "schema": { "type": "array", "items": { "type": "integer" } } } }, "stream": true } }, "response": { "status_code": 200, "headers": { "content-type": "text/event-stream; charset=utf-8", "connection": "keep-alive", "cache-control": "no-cache", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "cf-cache-status": "DYNAMIC", "x-robots-tag": "none", "content-security-policy": "default-src 'none'; frame-ancestors 'none'" }, "body": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-sonnet-4-5-20250929\",\"id\":\"msg_013nnniYDrJDocdy5nrMU7cH\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":135,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":1,\"service_tier\":\"standard\",\"inference_geo\":\"not_available\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"[\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"12\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"345,\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"67890]\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":135,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":10} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n" } } ]anthropic-sdk-python-0.120.2/tests/lib/_parse/__inline_snapshot__/test_messages/TestSyncParse/000077500000000000000000000000001523216435200325335ustar00rootroot00000000000000bd7029d5-d8d4-4e06-be3a-2ac9c60803a6.json000066400000000000000000000077431523216435200402760ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/_parse/__inline_snapshot__/test_messages/TestSyncParse[ { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "600", "accept": "application/json", "content-type": "application/json", "user-agent": "Anthropic/Python 0.82.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.82.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.9.18", "x-stainless-async": "false", "anthropic-version": "2023-06-01", "x-stainless-helper": "messages.parse", "x-stainless-retry-count": "0", "x-stainless-read-timeout": "600", "content-length": "749" }, "body": { "max_tokens": 1024, "messages": [ { "role": "user", "content": "Extract order: 2 Green Tea at $5.50 and 1 Coffee at $3.00. Total $14." } ], "model": "claude-sonnet-4-5", "output_config": { "format": { "schema": { "$defs": { "OrderItem": { "type": "object", "title": "OrderItem", "properties": { "product_name": { "type": "string", "title": "Product Name" }, "price": { "type": "number", "title": "Price" }, "quantity": { "type": "integer", "title": "Quantity" } }, "additionalProperties": false, "required": [ "product_name", "price", "quantity" ] } }, "type": "object", "title": "OrderDetails", "properties": { "items": { "type": "array", "title": "Items", "items": { "$ref": "#/$defs/OrderItem" } }, "total": { "type": "number", "title": "Total" } }, "additionalProperties": false, "required": [ "items", "total" ] }, "type": "json_schema" } } } }, "response": { "status_code": 200, "headers": { "content-type": "application/json", "connection": "keep-alive", "x-robots-tag": "none", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "content-security-policy": "default-src 'none'; frame-ancestors 'none'", "cf-cache-status": "DYNAMIC" }, "body": { "model": "claude-sonnet-4-5-20250929", "id": "msg_01T4jd6NyD9xGGtTPDC4ogy5", "type": "message", "role": "assistant", "content": [ { "type": "text", "text": "{\"items\":[{\"product_name\":\"Green Tea\",\"price\":5.50,\"quantity\":2},{\"product_name\":\"Coffee\",\"price\":3.00,\"quantity\":1}],\"total\":14.0}" } ], "stop_reason": "end_turn", "stop_sequence": null, "usage": { "input_tokens": 406, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": { "ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0 }, "output_tokens": 50, "service_tier": "standard", "inference_geo": "not_available" } } } } ]c11eefc7-fff0-4466-8453-42eef73b8876.json000066400000000000000000000061771523216435200402550ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/_parse/__inline_snapshot__/test_messages/TestSyncParse[ { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "600", "accept": "application/json", "content-type": "application/json", "user-agent": "Anthropic/Python 0.82.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.82.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.9.18", "x-stainless-async": "false", "anthropic-version": "2023-06-01", "x-stainless-helper": "messages.parse", "x-stainless-retry-count": "0", "x-stainless-read-timeout": "600", "content-length": "474" }, "body": { "max_tokens": 1024, "messages": [ { "role": "user", "content": "Extract: I want to order 2 Green Tea at $5.50 each" } ], "model": "claude-sonnet-4-5", "output_config": { "format": { "schema": { "type": "object", "title": "OrderItem", "properties": { "product_name": { "type": "string", "title": "Product Name" }, "price": { "type": "number", "title": "Price" }, "quantity": { "type": "integer", "title": "Quantity" } }, "additionalProperties": false, "required": [ "product_name", "price", "quantity" ] }, "type": "json_schema" } } } }, "response": { "status_code": 200, "headers": { "content-type": "application/json", "connection": "keep-alive", "x-robots-tag": "none", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "content-security-policy": "default-src 'none'; frame-ancestors 'none'", "cf-cache-status": "DYNAMIC" }, "body": { "model": "claude-sonnet-4-5-20250929", "id": "msg_01Egs18hRzhru3uGon3qesbA", "type": "message", "role": "assistant", "content": [ { "type": "text", "text": "{\"product_name\": \"Green Tea\", \"price\": 5.50, \"quantity\": 2}" } ], "stop_reason": "end_turn", "stop_sequence": null, "usage": { "input_tokens": 249, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": { "ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0 }, "output_tokens": 26, "service_tier": "standard", "inference_geo": "not_available" } } } } ]anthropic-sdk-python-0.120.2/tests/lib/_parse/test_beta_messages.py000066400000000000000000000107061523216435200253440ustar00rootroot00000000000000import json from typing import Any, cast import pytest from pydantic import BaseModel from inline_snapshot import external, snapshot from anthropic import AnthropicError, AsyncAnthropic, _compat from anthropic.types.beta.parsed_beta_message import ParsedBetaMessage @pytest.mark.skipif(_compat.PYDANTIC_V1, reason="tool runner not supported with pydantic v1") @pytest.mark.filterwarnings("ignore::DeprecationWarning") class TestAsyncMessages: @pytest.mark.parametrize( "http_snapshot", [ cast(Any, external("uuid:9381e2b7-7fa1-46f7-9f78-46dc8431ea9d.json")), ], ) async def test_stream_with_raw_schema(self, async_snapshot_client: AsyncAnthropic) -> None: async def async_stream_parse(client: AsyncAnthropic) -> ParsedBetaMessage[None]: async with client.beta.messages.stream( model="claude-sonnet-4-5", messages=[ { "role": "user", "content": "Extract order IDs from the following text:\n\nOrder 12345\nOrder 67890", } ], output_format={ "type": "json_schema", "schema": { "type": "array", "items": {"type": "integer"}, }, }, betas=["structured-outputs-2025-12-15"], max_tokens=1024, ) as stream: return await stream.get_final_message() response = await async_stream_parse(async_snapshot_client) assert response.content[0].type == "text" assert response.content[0].text == snapshot("[12345,67890]") @pytest.mark.parametrize( "http_snapshot", [ cast(Any, external("uuid:d7092a7d-b723-4470-8fb0-da138cd103a1.json")), ], ) async def test_parse_uses_output_config(self, async_snapshot_client: AsyncAnthropic) -> None: class User(BaseModel): name: str age: int response = await async_snapshot_client.beta.with_raw_response.messages.parse( model="claude-sonnet-4-5", messages=[ { "role": "user", "content": "Extract the user's name and age from the following text:\n\nMy name is John Doe and I am 30 years old.", } ], output_format=User, max_tokens=1024, ) request_json = json.loads(response.http_request.content) assert request_json == snapshot( { "max_tokens": 1024, "messages": [ { "role": "user", "content": """\ Extract the user's name and age from the following text: My name is John Doe and I am 30 years old.\ """, } ], "model": "claude-sonnet-4-5", "output_config": { "format": { "schema": { "type": "object", "title": "User", "properties": { "name": {"type": "string", "title": "Name"}, "age": {"type": "integer", "title": "Age"}, }, "additionalProperties": False, "required": ["name", "age"], }, "type": "json_schema", } }, } ) async def test_rejects_both_output_format_and_config(self, async_client: AsyncAnthropic) -> None: class User(BaseModel): name: str age: int with pytest.raises(AnthropicError, match="Both output_format and output_config.format were provided"): await async_client.beta.messages.parse( model="claude-sonnet-4-5", messages=[ { "role": "user", "content": "Extract the user's name and age.", } ], output_format=User, output_config={ "format": { "type": "json_schema", "schema": {"type": "object"}, } }, max_tokens=1024, ) anthropic-sdk-python-0.120.2/tests/lib/_parse/test_messages.py000066400000000000000000000134511523216435200243510ustar00rootroot00000000000000from typing import Any, List, cast import pytest from pydantic import BaseModel from inline_snapshot import external, snapshot from anthropic import Anthropic, AsyncAnthropic, _compat class OrderItem(BaseModel): product_name: str price: float quantity: int class OrderDetails(BaseModel): items: List[OrderItem] total: float @pytest.mark.skipif(_compat.PYDANTIC_V1, reason="structured outputs not supported with pydantic v1") class TestSyncParse: @pytest.mark.parametrize( "http_snapshot", [ cast(Any, external("uuid:c11eefc7-fff0-4466-8453-42eef73b8876.json")), ], ) def test_parse_with_pydantic_model(self, snapshot_client: Anthropic) -> None: """Test sync messages.parse() with a Pydantic model output_format.""" response = snapshot_client.messages.parse( model="claude-sonnet-4-5", messages=[ { "role": "user", "content": "Extract: I want to order 2 Green Tea at $5.50 each", } ], output_format=OrderItem, max_tokens=1024, ) assert response.parsed_output is not None assert isinstance(response.parsed_output, OrderItem) assert response.parsed_output.product_name == "Green Tea" assert response.parsed_output.price == 5.5 assert response.parsed_output.quantity == 2 @pytest.mark.parametrize( "http_snapshot", [ cast(Any, external("uuid:bd7029d5-d8d4-4e06-be3a-2ac9c60803a6.json")), ], ) def test_parse_with_nested_pydantic_model(self, snapshot_client: Anthropic) -> None: """Test sync messages.parse() with nested Pydantic models.""" response = snapshot_client.messages.parse( model="claude-sonnet-4-5", messages=[ { "role": "user", "content": "Extract order: 2 Green Tea at $5.50 and 1 Coffee at $3.00. Total $14.", } ], output_format=OrderDetails, max_tokens=1024, ) assert response.parsed_output is not None assert isinstance(response.parsed_output, OrderDetails) assert len(response.parsed_output.items) == 2 assert response.parsed_output.items[0].product_name == "Green Tea" assert response.parsed_output.total == 14.0 @pytest.mark.skipif(_compat.PYDANTIC_V1, reason="structured outputs not supported with pydantic v1") class TestAsyncParse: @pytest.mark.parametrize( "http_snapshot", [ cast(Any, external("uuid:924cfccc-c863-44ed-9838-c36b37416eaf.json")), ], ) async def test_parse_with_pydantic_model(self, async_snapshot_client: AsyncAnthropic) -> None: """Test async messages.parse() with a Pydantic model output_format.""" response = await async_snapshot_client.messages.parse( model="claude-sonnet-4-5", messages=[ { "role": "user", "content": "Extract: I want to order 2 Green Tea at $5.50 each", } ], output_format=OrderItem, max_tokens=1024, ) assert response.parsed_output is not None assert isinstance(response.parsed_output, OrderItem) assert response.parsed_output.product_name == "Green Tea" assert response.parsed_output.price == 5.5 assert response.parsed_output.quantity == 2 @pytest.mark.parametrize( "http_snapshot", [ cast(Any, external("uuid:d743d628-16aa-4c9f-ae78-51deb578f746.json")), ], ) async def test_parse_with_nested_pydantic_model(self, async_snapshot_client: AsyncAnthropic) -> None: """Test async messages.parse() with nested Pydantic models.""" response = await async_snapshot_client.messages.parse( model="claude-sonnet-4-5", messages=[ { "role": "user", "content": "Extract order: 2 Green Tea at $5.50 and 1 Coffee at $3.00. Total $14.", } ], output_format=OrderDetails, max_tokens=1024, ) assert response.parsed_output is not None assert isinstance(response.parsed_output, OrderDetails) assert len(response.parsed_output.items) == 2 assert response.parsed_output.items[0].product_name == "Green Tea" assert response.parsed_output.total == 14.0 @pytest.mark.skipif(_compat.PYDANTIC_V1, reason="structured outputs not supported with pydantic v1") class TestAsyncStream: @pytest.mark.parametrize( "http_snapshot", [ cast(Any, external("uuid:a8b7dbb8-2321-4624-abdb-7ba2136b5c38.json")), ], ) async def test_stream_with_raw_schema(self, async_snapshot_client: AsyncAnthropic) -> None: """Test async messages.stream() with raw JSON schema via output_config.""" async with async_snapshot_client.messages.stream( model="claude-sonnet-4-5", messages=[ { "role": "user", "content": "Extract order IDs from the following text:\n\nOrder 12345\nOrder 67890", } ], output_config={ "format": { "type": "json_schema", "schema": { "type": "array", "items": {"type": "integer"}, }, }, }, max_tokens=1024, ) as stream: response = await stream.get_final_message() content_block = response.content[0] assert content_block.type == "text" assert content_block.text == snapshot("[12345,67890]") anthropic-sdk-python-0.120.2/tests/lib/_parse/test_transform.py000066400000000000000000000132541523216435200245560ustar00rootroot00000000000000from copy import deepcopy import pytest from inline_snapshot import snapshot from anthropic.lib._parse._transform import transform_schema def test_ref_schema(): schema = {"$ref": "#/components/schemas/SomeSchema"} result = transform_schema(schema) assert result == snapshot({"$ref": "#/components/schemas/SomeSchema"}) def test_ref_schema_with_defs(): # Pydantic v2 emits this shape for `RootModel` types: a root-level # `$ref` with sibling `$defs` holding the referenced schema. schema = { "$ref": "#/$defs/Tier", "$defs": { "Tier": { "type": "string", "enum": ["free", "pro", "enterprise"], "title": "Tier", } }, } result = transform_schema(schema) assert result == snapshot( { "$defs": { "Tier": { "type": "string", "enum": ["free", "pro", "enterprise"], "title": "Tier", } }, "$ref": "#/$defs/Tier", } ) def test_anyof_schema(): schema = { "anyOf": [ {"type": "string"}, {"type": "integer", "minimum": 1}, ] } result = transform_schema(schema) assert result == snapshot( { "anyOf": [ {"type": "string"}, { "type": "integer", "description": "{minimum: 1}", }, ] } ) def test_enum_schema(): schema = { "type": "string", "enum": [ "foo", "bar", ], } result = transform_schema(schema) assert result == snapshot({"type": "string", "enum": ["foo", "bar"]}) def test_allof(): schema = { "allOf": [ {"type": "object", "properties": {"name": {"type": "string"}}}, {"type": "object", "properties": {"age": {"type": "integer", "minimum": 0}}}, ] } result = transform_schema(schema) assert result == snapshot( { "allOf": [ { "type": "object", "properties": {"name": {"type": "string"}}, "additionalProperties": False, }, { "type": "object", "properties": {"age": {"type": "integer", "description": "{minimum: 0}"}}, "additionalProperties": False, }, ] } ) def test_object_schema(): schema = { "type": "object", "properties": { "name": {"type": "string", "default": "John"}, "age": {"type": "integer", "minimum": 0}, }, "required": ["name"], "description": "Person object", } result = transform_schema(schema) assert result == snapshot( { "type": "object", "description": "Person object", "properties": { "name": {"type": "string", "description": "{default: John}"}, "age": {"type": "integer", "description": "{minimum: 0}"}, }, "additionalProperties": False, "required": ["name"], } ) def test_array_schema(): schema = { "type": "array", "items": {"type": "string"}, "minItems": 2, "description": "A list of strings", } result = transform_schema(schema) assert result == snapshot( { "type": "array", "description": """\ A list of strings {minItems: 2}\ """, "items": {"type": "string"}, } ) def test_string_schema_with_format_and_default(): schema = { "type": "string", "format": "email", "default": "user@example.com", "description": "User email", } result = transform_schema(schema) assert result == snapshot( { "type": "string", "description": """\ User email {default: user@example.com}\ """, "format": "email", } ) def test_string_schema_without_format(): schema = {"type": "string"} result = transform_schema(schema) assert result == snapshot({"type": "string"}) def test_integer_schema_with_min_max_exclusive(): schema = { "type": "integer", "minimum": 1, "maximum": 10, "exclusiveMinimum": 0, "exclusiveMaximum": 20, "description": "A number", } result = transform_schema(schema) assert result == snapshot( { "type": "integer", "description": """\ A number {minimum: 1, maximum: 10, exclusiveMinimum: 0, exclusiveMaximum: 20}\ """, } ) def test_boolean_schema(): schema = {"type": "boolean", "description": "A flag"} result = transform_schema(schema) assert result == snapshot({"type": "boolean", "description": "A flag"}) def test_null_schema(): schema = {"type": "null"} result = transform_schema(schema) assert result == snapshot({"type": "null"}) def test_unsupported_type_asserts(): schema = {"type": "unsupported"} with pytest.raises(AssertionError): transform_schema(schema) def test_original_schema_not_mutated(): original_schema = { "type": "object", "properties": { "name": {"type": "string", "default": "John"}, "age": {"type": "integer", "minimum": 0}, }, "required": ["name"], "description": "Person object", "additionalProperties": True, } original_schema_backup = deepcopy(original_schema) transform_schema(original_schema) assert original_schema == original_schema_backup anthropic-sdk-python-0.120.2/tests/lib/environments/000077500000000000000000000000001523216435200224035ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/environments/__init__.py000066400000000000000000000000001523216435200245020ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/environments/test_poller.py000066400000000000000000000011731523216435200253130ustar00rootroot00000000000000from __future__ import annotations import pytest from anthropic.lib.environments._poller import _jitter, _backoff @pytest.mark.parametrize( ("description", "attempt", "want"), [ ("first failure backs off two seconds", 1, 2.0), ("second failure doubles to four seconds", 2, 4.0), ("very large attempt is capped at sixty seconds", 100, 60.0), ], ) def test_backoff(description: str, attempt: int, want: float) -> None: assert _backoff(attempt) == want, description def test_jitter_within_bounds() -> None: for _ in range(100): v = _jitter(1.0, 3.0) assert 1.0 <= v < 3.0 anthropic-sdk-python-0.120.2/tests/lib/environments/test_poller_method.py000066400000000000000000000357771523216435200266740ustar00rootroot00000000000000"""Tests for ``iter_work`` / ``aiter_work`` (the implementations behind ``client.beta.environments.work.poller()``). We don't need a real HTTP mock here because the generators only ever talk to a ``Work``/``AsyncWork`` resource through three methods (``poll``, ``ack``, ``stop``). The fakes below stand in for that resource and let each test feed a script of poll responses while recording ack/stop call sites. """ from __future__ import annotations import time import asyncio from typing import Any, cast from collections.abc import AsyncIterator import httpx import pytest from anthropic import APIStatusError from anthropic.lib.environments._poller import iter_work, aiter_work class _StubWorkData: type = "session" class _StubWork: """Minimal stand-in for ``BetaSelfHostedWork`` — only fields the poller reads.""" def __init__(self, *, id: str = "work_1") -> None: self.id = id self.data = _StubWorkData() def _api_status_error(code: int) -> APIStatusError: request = httpx.Request("POST", "https://api.example/poll") response = httpx.Response(status_code=code, request=request, content=b"{}") return APIStatusError("boom", response=response, body=None) class FakeWork: """Sync resource fake. ``poll_script`` is consumed in order; values are either ``BetaSelfHostedWork``-shaped stubs, ``None`` (no work available), or ``Exception`` instances (raised from poll). """ def __init__(self, poll_script: list[Any]) -> None: self._poll_script = list(poll_script) self.poll_calls: list[dict[str, Any]] = [] self.ack_calls: list[tuple[str, dict[str, Any]]] = [] self.stop_calls: list[tuple[str, dict[str, Any]]] = [] def poll(self, environment_id: str, **kwargs: Any) -> Any: self.poll_calls.append({"environment_id": environment_id, **kwargs}) if not self._poll_script: raise _StopTest("poll script exhausted") nxt = self._poll_script.pop(0) if isinstance(nxt, BaseException): raise nxt return nxt def ack(self, work_id: str, **kwargs: Any) -> None: self.ack_calls.append((work_id, kwargs)) def stop(self, work_id: str, **kwargs: Any) -> None: self.stop_calls.append((work_id, kwargs)) class FakeAsyncWork: def __init__(self, poll_script: list[Any]) -> None: self._poll_script = list(poll_script) self.poll_calls: list[dict[str, Any]] = [] self.ack_calls: list[tuple[str, dict[str, Any]]] = [] self.stop_calls: list[tuple[str, dict[str, Any]]] = [] async def poll(self, environment_id: str, **kwargs: Any) -> Any: self.poll_calls.append({"environment_id": environment_id, **kwargs}) if not self._poll_script: raise _StopTest("poll script exhausted") nxt = self._poll_script.pop(0) if isinstance(nxt, BaseException): raise nxt return nxt async def ack(self, work_id: str, **kwargs: Any) -> None: self.ack_calls.append((work_id, kwargs)) async def stop(self, work_id: str, **kwargs: Any) -> None: self.stop_calls.append((work_id, kwargs)) class _StopTest(BaseException): """Sentinel used to break a poller out of its infinite loop in tests. Inherits from BaseException so it bypasses the generator's ``except Exception`` arms (which would otherwise treat the empty-script error as a transient poll failure and retry forever). """ def _sync_noop(_seconds: float) -> None: return None async def _async_noop(_seconds: float) -> None: return None @pytest.fixture(autouse=True) def _no_sleep(monkeypatch: pytest.MonkeyPatch) -> None: # pyright: ignore[reportUnusedFunction] monkeypatch.setattr(time, "sleep", _sync_noop) monkeypatch.setattr(asyncio, "sleep", _async_noop) def _drain_one(it: Any) -> Any: """Pull a single item from a sync iterator; assert there was one.""" try: return next(it) except _StopTest: pytest.fail("poller exhausted script before yielding any work") async def _adrain_one(ait: AsyncIterator[Any]) -> Any: try: return await ait.__anext__() except _StopTest: pytest.fail("poller exhausted script before yielding any work") def test_iter_work_yields_acks_and_stops_one_item() -> None: work = _StubWork() fake = FakeWork(poll_script=[work]) it = iter_work(cast(Any, fake), environment_id="env_1") item = _drain_one(it) assert item is work assert fake.ack_calls == [("work_1", fake.ack_calls[0][1])] assert fake.stop_calls == [], "stop should not be called until body returns" with pytest.raises(_StopTest): next(it) assert len(fake.stop_calls) == 1 assert fake.stop_calls[0][0] == "work_1" def test_iter_work_calls_stop_when_body_raises() -> None: work = _StubWork(id="work_boom") fake = FakeWork(poll_script=[work]) it = iter_work(cast(Any, fake), environment_id="env_1") next(it) # Throwing into the generator simulates the consumer's body raising. # iter_work returns Iterator publicly but is a Generator internally. with pytest.raises(RuntimeError): cast(Any, it).throw(RuntimeError("body failed")) assert fake.stop_calls == [("work_boom", fake.stop_calls[0][1])] def test_iter_work_backs_off_on_transient_error() -> None: fake = FakeWork(poll_script=[_api_status_error(500), _StubWork(id="work_2")]) it = iter_work(cast(Any, fake), environment_id="env_1") item = _drain_one(it) assert item.id == "work_2" assert len(fake.poll_calls) == 2 def test_iter_work_raises_on_permanent_4xx() -> None: fake = FakeWork(poll_script=[_api_status_error(401)]) it = iter_work(cast(Any, fake), environment_id="env_1") with pytest.raises(APIStatusError): next(it) def test_iter_work_backs_off_on_httpx_transport_error() -> None: """A raw ``httpx`` transport error (not wrapped in an SDK ``APIError``) is still transient and must be retried, not propagated.""" fake = FakeWork(poll_script=[httpx.ConnectError("connection refused"), _StubWork(id="work_2")]) it = iter_work(cast(Any, fake), environment_id="env_1") item = _drain_one(it) assert item.id == "work_2" assert len(fake.poll_calls) == 2 def test_iter_work_propagates_non_api_error_instead_of_retrying() -> None: """A programming error (here ``KeyError``) is not a transient API/transport failure, so it must propagate immediately rather than be swallowed and retried forever. ``poll`` is only called once — no backoff/retry.""" fake = FakeWork(poll_script=[KeyError("bug"), _StubWork(id="work_2")]) it = iter_work(cast(Any, fake), environment_id="env_1") with pytest.raises(KeyError): next(it) assert len(fake.poll_calls) == 1 def test_iter_work_propagates_non_api_error_from_ack() -> None: """Same for a non-API error raised by ``ack`` — it propagates rather than backing off and retrying.""" fake = FakeWork(poll_script=[_StubWork(id="work_bad")]) def _ack(_work_id: str, **_kwargs: Any) -> None: raise RuntimeError("ack bug") fake.ack = _ack # type: ignore[method-assign] it = iter_work(cast(Any, fake), environment_id="env_1") with pytest.raises(RuntimeError, match="ack bug"): next(it) assert fake.stop_calls == [] def test_iter_work_force_stops_on_permanent_ack_failure() -> None: """A permanent 4xx on ack force-stops the item rather than re-delivering it.""" fake = FakeWork(poll_script=[_StubWork(id="work_bad")]) def _ack(_work_id: str, **_kwargs: Any) -> None: raise _api_status_error(403) fake.ack = _ack # type: ignore[method-assign] it = iter_work(cast(Any, fake), environment_id="env_1") with pytest.raises(_StopTest): next(it) assert [c[0] for c in fake.stop_calls] == ["work_bad"] assert fake.stop_calls[0][1]["force"] is True def test_iter_work_drain_returns_on_empty_queue() -> None: a, b = _StubWork(id="work_a"), _StubWork(id="work_b") fake = FakeWork(poll_script=[a, b, None]) it = iter_work(cast(Any, fake), environment_id="env_1", drain=True) items = list(it) assert [i.id for i in items] == ["work_a", "work_b"] # Generator returned cleanly — no _StopTest raised, script not exhausted. assert len(fake.poll_calls) == 3 def test_iter_work_drain_returns_immediately_when_queue_empty() -> None: fake = FakeWork(poll_script=[None]) it = iter_work(cast(Any, fake), environment_id="env_1", drain=True) assert list(it) == [] assert len(fake.poll_calls) == 1 def test_iter_work_auto_stop_false_never_stops() -> None: """A dispatcher hands work off to another process that owns the stop call. The poller must not stop the lease out from under that process. """ a, b = _StubWork(id="work_a"), _StubWork(id="work_b") fake = FakeWork(poll_script=[a, b, None]) it = iter_work(cast(Any, fake), environment_id="env_1", drain=True, auto_stop=False) assert [item.id for item in it] == ["work_a", "work_b"] assert [c[0] for c in fake.ack_calls] == ["work_a", "work_b"], "every item should still be ack'd" assert fake.stop_calls == [] def test_iter_work_auto_stop_false_does_not_stop_on_body_raise() -> None: work = _StubWork(id="work_boom") fake = FakeWork(poll_script=[work]) it = iter_work(cast(Any, fake), environment_id="env_1", auto_stop=False) next(it) with pytest.raises(RuntimeError): cast(Any, it).throw(RuntimeError("body failed")) assert fake.stop_calls == [] def test_iter_work_forwards_reclaim_older_than_ms() -> None: fake = FakeWork(poll_script=[None]) it = iter_work(cast(Any, fake), environment_id="env_1", drain=True, reclaim_older_than_ms=2000) list(it) assert fake.poll_calls[0]["reclaim_older_than_ms"] == 2000 def test_iter_work_block_ms_none_omits_param() -> None: """The server rejects block_ms=0; None must translate to the omit sentinel so the poll is non-blocking instead of 400ing.""" from anthropic._types import omit fake = FakeWork(poll_script=[None]) it = iter_work(cast(Any, fake), environment_id="env_1", drain=True, block_ms=None) list(it) assert fake.poll_calls[0]["block_ms"] is omit @pytest.mark.asyncio() async def test_aiter_work_yields_acks_and_stops_one_item() -> None: work = _StubWork() fake = FakeAsyncWork(poll_script=[work]) ait = aiter_work(cast(Any, fake), environment_id="env_1") item = await _adrain_one(ait) assert item is work assert fake.ack_calls == [("work_1", fake.ack_calls[0][1])] assert fake.stop_calls == [] with pytest.raises(_StopTest): await ait.__anext__() assert len(fake.stop_calls) == 1 @pytest.mark.asyncio() async def test_aiter_work_calls_stop_when_body_raises() -> None: work = _StubWork(id="work_boom") fake = FakeAsyncWork(poll_script=[work]) ait = aiter_work(cast(Any, fake), environment_id="env_1") await ait.__anext__() with pytest.raises(RuntimeError): await cast(Any, ait).athrow(RuntimeError("body failed")) assert fake.stop_calls == [("work_boom", fake.stop_calls[0][1])] @pytest.mark.asyncio() async def test_aiter_work_backs_off_on_transient_error() -> None: fake = FakeAsyncWork(poll_script=[_api_status_error(500), _StubWork(id="work_2")]) ait = aiter_work(cast(Any, fake), environment_id="env_1") item = await _adrain_one(ait) assert item.id == "work_2" assert len(fake.poll_calls) == 2 async def test_aiter_work_propagates_non_api_error_instead_of_retrying() -> None: """Async counterpart: a non-API/non-transport error propagates instead of being retried forever.""" fake = FakeAsyncWork(poll_script=[AttributeError("bug"), _StubWork(id="work_2")]) ait = aiter_work(cast(Any, fake), environment_id="env_1") with pytest.raises(AttributeError): await ait.__anext__() assert len(fake.poll_calls) == 1 @pytest.mark.asyncio() async def test_aiter_work_drain_auto_stop_false_dispatch_shape() -> None: a, b = _StubWork(id="work_a"), _StubWork(id="work_b") fake = FakeAsyncWork(poll_script=[a, b, None]) ait = aiter_work(cast(Any, fake), environment_id="env_1", drain=True, auto_stop=False) items = [item async for item in ait] assert [i.id for i in items] == ["work_a", "work_b"] assert [c[0] for c in fake.ack_calls] == ["work_a", "work_b"] assert fake.stop_calls == [] assert len(fake.poll_calls) == 3 # ---------- extra_headers per-request passthrough --------------------------- # # These assert the caller-supplied ``extra_headers`` actually reaches every # underlying ``poll`` / ``ack`` / ``stop`` call (the resource methods route it # through ``make_request_options``). The fakes record the kwargs each method # was called with, so a missing thread-through shows up as ``None``. _EXTRA = {"x-trace-id": "trace-123"} def test_iter_work_threads_extra_headers_into_poll_ack_stop() -> None: fake = FakeWork(poll_script=[_StubWork(id="work_h")]) it = iter_work(cast(Any, fake), environment_id="env_1", extra_headers=_EXTRA) item = _drain_one(it) assert item.id == "work_h" assert fake.poll_calls[0]["extra_headers"] == _EXTRA assert fake.ack_calls[0][1]["extra_headers"] == _EXTRA with pytest.raises(_StopTest): next(it) assert fake.stop_calls[0][1]["extra_headers"] == _EXTRA def test_iter_work_threads_extra_headers_into_force_stop() -> None: """A permanent ack failure force-stops the item; the passthrough header must ride along on that force-stop call too.""" fake = FakeWork(poll_script=[_StubWork(id="work_bad")]) def _ack(_work_id: str, **_kwargs: Any) -> None: raise _api_status_error(403) fake.ack = _ack # type: ignore[method-assign] it = iter_work(cast(Any, fake), environment_id="env_1", extra_headers=_EXTRA) with pytest.raises(_StopTest): next(it) assert fake.stop_calls[0][0] == "work_bad" assert fake.stop_calls[0][1]["force"] is True assert fake.stop_calls[0][1]["extra_headers"] == _EXTRA @pytest.mark.asyncio() async def test_aiter_work_threads_extra_headers_into_poll_ack_stop() -> None: fake = FakeAsyncWork(poll_script=[_StubWork(id="work_h")]) ait = aiter_work(cast(Any, fake), environment_id="env_1", extra_headers=_EXTRA) item = await _adrain_one(ait) assert item.id == "work_h" assert fake.poll_calls[0]["extra_headers"] == _EXTRA assert fake.ack_calls[0][1]["extra_headers"] == _EXTRA with pytest.raises(_StopTest): await ait.__anext__() assert fake.stop_calls[0][1]["extra_headers"] == _EXTRA @pytest.mark.asyncio() async def test_aiter_work_threads_extra_headers_into_force_stop() -> None: fake = FakeAsyncWork(poll_script=[_StubWork(id="work_bad")]) async def _ack(_work_id: str, **_kwargs: Any) -> None: raise _api_status_error(403) fake.ack = _ack # type: ignore[method-assign] ait = aiter_work(cast(Any, fake), environment_id="env_1", extra_headers=_EXTRA) with pytest.raises(_StopTest): await ait.__anext__() assert fake.stop_calls[0][0] == "work_bad" assert fake.stop_calls[0][1]["force"] is True assert fake.stop_calls[0][1]["extra_headers"] == _EXTRA anthropic-sdk-python-0.120.2/tests/lib/environments/test_worker.py000066400000000000000000000522531523216435200253340ustar00rootroot00000000000000"""Tests for :class:`EnvironmentWorker`. The worker composes the control-plane poller, the per-session ``AgentToolContext`` / skill download, the lease heartbeat, and the session tool runner. We stub ``aiter_work``, the session tool runner, and the worker's ``_copy_client_with_bearer_auth`` helper so each test can drive a single claimed work item and assert the surrounding plumbing (skip non-session work, heartbeat the lease, force-stop on exit) — via both the ``run()`` poll loop and the single-item ``handle_item()`` entry point. After the auth refactor, heartbeat / force-stop traffic flows through a Bearer-only sub-client the worker constructs via the shared ``_copy_client_with_bearer_auth`` util. The tests intercept that helper so they can route those calls to a recording fake without spinning up a real ``AsyncAnthropic`` (and the httpx pool that comes with it). """ from __future__ import annotations import os import asyncio import contextlib from types import SimpleNamespace from typing import Any from collections.abc import AsyncIterator from typing_extensions import override import pytest from anthropic import Anthropic, AsyncAnthropic from anthropic._compat import PYDANTIC_V1 from anthropic.lib.environments import _worker as worker_mod from anthropic.lib.environments._worker import EnvironmentWorker class _FakeWorkResource: def __init__(self, *, heartbeat_state: str = "stopping") -> None: self._heartbeat_state = heartbeat_state self.heartbeat_calls: list[dict[str, Any]] = [] self.stop_calls: list[dict[str, Any]] = [] async def heartbeat( self, work_id: str, *, environment_id: str, expected_last_heartbeat: str, # noqa: ARG002 extra_headers: Any = None, ) -> Any: self.heartbeat_calls.append( {"work_id": work_id, "environment_id": environment_id, "extra_headers": extra_headers} ) return SimpleNamespace(last_heartbeat="hb-1", ttl_seconds=60, state=self._heartbeat_state, lease_extended=True) async def stop( self, work_id: str, *, environment_id: str, force: bool = False, extra_headers: Any = None, betas: Any = None, ) -> None: self.stop_calls.append( { "work_id": work_id, "environment_id": environment_id, "force": force, "extra_headers": extra_headers, "betas": betas, } ) class _FakeSessions: def __init__(self) -> None: self.retrieve_calls: list[str] = [] async def retrieve(self, session_id: str, *, betas: Any = None) -> Any: # noqa: ARG002 self.retrieve_calls.append(session_id) return SimpleNamespace(agent=SimpleNamespace(skills=[])) def _fake_client(work: _FakeWorkResource, sessions: _FakeSessions) -> Any: return SimpleNamespace( beta=SimpleNamespace(sessions=sessions, environments=SimpleNamespace(work=work)), ) def _install_scoped_client( monkeypatch: pytest.MonkeyPatch, work: _FakeWorkResource, sessions: _FakeSessions | None = None ) -> list[dict[str, Any]]: """Intercept the shared bearer-auth client factory. Returns the list of args the factory was called with so a test can assert on the helper-telemetry tag (one entry per call: poll, worker, …). """ calls: list[dict[str, Any]] = [] if sessions is None: sessions = _FakeSessions() fake_scoped = SimpleNamespace(beta=SimpleNamespace(sessions=sessions, environments=SimpleNamespace(work=work))) def fake_factory(client: Any, *, auth_token: str, helper: str) -> Any: # noqa: ARG001 calls.append({"auth_token": auth_token, "helper": helper}) return fake_scoped monkeypatch.setattr(worker_mod, "_copy_client_with_bearer_auth", fake_factory) return calls def _work_item(*, work_type: str = "session", work_id: str = "w_1", session_id: str = "s_1") -> Any: return SimpleNamespace(id=work_id, environment_id="e_1", data=SimpleNamespace(type=work_type, id=session_id)) def _install_aiter_work(monkeypatch: pytest.MonkeyPatch, items: list[Any]) -> None: async def fake_aiter_work(_work: Any, **_kw: Any) -> AsyncIterator[Any]: for it in items: yield it monkeypatch.setattr(worker_mod, "aiter_work", fake_aiter_work) def _install_run_session_tools(monkeypatch: pytest.MonkeyPatch, record: dict[str, Any]) -> None: @contextlib.asynccontextmanager async def fake_run_session_tools( _client: Any, session_id: str, *, tools: Any, max_idle: Any = None, environment_key: Any = None, extra_headers: Any = None, ): record["run"] = { "session_id": session_id, "tools": tools, "max_idle": max_idle, "environment_key": environment_key, "extra_headers": extra_headers, } async def _iter() -> AsyncIterator[Any]: # The session completes on its own (no tool calls). The run then # ends via the normal session-completion path; the heartbeat keeps # the lease alive throughout and is stopped on the way out. return yield # pragma: no cover (makes this an async generator function) yield _iter() monkeypatch.setattr(worker_mod, "_run_session_tools", fake_run_session_tools) @pytest.mark.skipif(PYDANTIC_V1, reason="tool functions are only supported with pydantic v2") @pytest.mark.asyncio() async def test_environment_worker_serves_session(monkeypatch: pytest.MonkeyPatch) -> None: work = _FakeWorkResource(heartbeat_state="running") sessions = _FakeSessions() client = _fake_client(work, sessions) _install_aiter_work(monkeypatch, [_work_item()]) scoped_calls = _install_scoped_client(monkeypatch, work, sessions) record: dict[str, Any] = {} _install_run_session_tools(monkeypatch, record) worker = EnvironmentWorker( client=client, environment_id="e_1", environment_key="env_key", workdir=".", max_idle=12.0, ) await asyncio.wait_for(worker.run(), timeout=5) # AgentToolContext set up skills for the claimed session. assert sessions.retrieve_calls == ["s_1"] # The session tool runner ran with the right session + max_idle, the # environment key was threaded through, and the default toolset (a list of 6 # tools) was bound. assert record["run"]["session_id"] == "s_1" assert record["run"]["max_idle"] == 12.0 assert record["run"]["environment_key"] == "env_key" assert [t.name for t in record["run"]["tools"]] == ["bash", "read", "write", "edit", "glob", "grep"] # The lease was heartbeated. assert len(work.heartbeat_calls) >= 1 # The work item was force-stopped on exit. assert len(work.stop_calls) == 1 assert work.stop_calls[0]["work_id"] == "w_1" assert work.stop_calls[0]["force"] is True # Auth flows through scoped sub-clients tagged with the right helper. # ``run()`` builds an ``environments-work-poller``-tagged client for ``aiter_work``; # each handled item builds an ``environments-worker``-tagged client for the # heartbeat and force-stop. The environment key flows into both. assert scoped_calls == [ {"auth_token": "env_key", "helper": "environments-work-poller"}, {"auth_token": "env_key", "helper": "environments-worker"}, ] @pytest.mark.asyncio() async def test_environment_worker_accepts_tools_factory(monkeypatch: pytest.MonkeyPatch) -> None: work = _FakeWorkResource(heartbeat_state="running") sessions = _FakeSessions() client = _fake_client(work, sessions) _install_aiter_work(monkeypatch, [_work_item()]) _install_scoped_client(monkeypatch, work, sessions) record: dict[str, Any] = {} _install_run_session_tools(monkeypatch, record) sentinel = SimpleNamespace(name="custom") def factory(_env: Any) -> list[Any]: return [sentinel] worker = EnvironmentWorker( client=client, environment_id="e_1", environment_key="env_key", tools=factory, ) await asyncio.wait_for(worker.run(), timeout=5) assert record["run"]["tools"] == [sentinel] @pytest.mark.asyncio() async def test_run_requires_environment_id_and_environment_key() -> None: work = _FakeWorkResource() sessions = _FakeSessions() client = _fake_client(work, sessions) worker = EnvironmentWorker(client, workdir=".") with pytest.raises(ValueError, match="environment_id and environment_key are required"): await worker.run() @pytest.mark.skipif(PYDANTIC_V1, reason="tool functions are only supported with pydantic v2") @pytest.mark.asyncio() async def test_handle_item_services_a_single_claimed_item(monkeypatch: pytest.MonkeyPatch) -> None: work = _FakeWorkResource(heartbeat_state="running") sessions = _FakeSessions() client = _fake_client(work, sessions) scoped_calls = _install_scoped_client(monkeypatch, work, sessions) record: dict[str, Any] = {} _install_run_session_tools(monkeypatch, record) # handle_item should not poll for work at all. _install_aiter_work(monkeypatch, []) # No environment_id needed for the single-item flow. worker = EnvironmentWorker(client, workdir=".", max_idle=7.0) await asyncio.wait_for( worker.handle_item(work_id="w_1", environment_id="e_1", session_id="s_1", environment_key="env_key"), timeout=5, ) assert sessions.retrieve_calls == ["s_1"] assert record["run"]["session_id"] == "s_1" assert record["run"]["max_idle"] == 7.0 assert record["run"]["environment_key"] == "env_key" assert len(work.heartbeat_calls) >= 1 # Auth lives on the scoped sub-client now, not in extra_headers — so the # heartbeat goes out with no per-call extras (None) unless the worker was # constructed with passthrough headers. assert work.heartbeat_calls[0] == { "work_id": "w_1", "environment_id": "e_1", "extra_headers": None, } assert len(work.stop_calls) == 1 assert work.stop_calls[0]["work_id"] == "w_1" assert work.stop_calls[0]["force"] is True # ``handle_item`` doesn't poll, so only the heartbeat/force-stop scoped # client is built — tagged ``environments-worker``. assert scoped_calls == [{"auth_token": "env_key", "helper": "environments-worker"}] @pytest.mark.skipif(PYDANTIC_V1, reason="tool functions are only supported with pydantic v2") @pytest.mark.asyncio() async def test_handle_item_falls_back_to_env_vars(monkeypatch: pytest.MonkeyPatch) -> None: work = _FakeWorkResource(heartbeat_state="running") sessions = _FakeSessions() client = _fake_client(work, sessions) scoped_calls = _install_scoped_client(monkeypatch, work, sessions) record: dict[str, Any] = {} _install_run_session_tools(monkeypatch, record) monkeypatch.setenv("ANTHROPIC_WORK_ID", "w_env") monkeypatch.setenv("ANTHROPIC_ENVIRONMENT_ID", "e_env") monkeypatch.setenv("ANTHROPIC_SESSION_ID", "s_env") monkeypatch.setenv("ANTHROPIC_ENVIRONMENT_KEY", "key_env") worker = EnvironmentWorker(client, workdir=".") await asyncio.wait_for(worker.handle_item(), timeout=5) assert sessions.retrieve_calls == ["s_env"] assert record["run"]["session_id"] == "s_env" assert record["run"]["environment_key"] == "key_env" assert work.heartbeat_calls[0] == { "work_id": "w_env", "environment_id": "e_env", "extra_headers": None, } assert work.stop_calls[0]["work_id"] == "w_env" assert scoped_calls == [{"auth_token": "key_env", "helper": "environments-worker"}] @pytest.mark.skipif(PYDANTIC_V1, reason="tool functions are only supported with pydantic v2") @pytest.mark.asyncio() async def test_handle_item_uses_constructor_environment_key(monkeypatch: pytest.MonkeyPatch) -> None: """``environment_key`` resolves to the worker's own key when not passed and no env var is set.""" work = _FakeWorkResource(heartbeat_state="running") sessions = _FakeSessions() client = _fake_client(work, sessions) scoped_calls = _install_scoped_client(monkeypatch, work, sessions) record: dict[str, Any] = {} _install_run_session_tools(monkeypatch, record) monkeypatch.delenv("ANTHROPIC_ENVIRONMENT_KEY", raising=False) worker = EnvironmentWorker(client, environment_key="ctor_key", workdir=".") await asyncio.wait_for( worker.handle_item(work_id="w_1", environment_id="e_1", session_id="s_1"), timeout=5, ) assert record["run"]["environment_key"] == "ctor_key" assert scoped_calls == [{"auth_token": "ctor_key", "helper": "environments-worker"}] @pytest.mark.asyncio() async def test_handle_item_missing_required_raises(monkeypatch: pytest.MonkeyPatch) -> None: work = _FakeWorkResource() sessions = _FakeSessions() client = _fake_client(work, sessions) for var in ("ANTHROPIC_WORK_ID", "ANTHROPIC_ENVIRONMENT_ID", "ANTHROPIC_SESSION_ID", "ANTHROPIC_ENVIRONMENT_KEY"): monkeypatch.delenv(var, raising=False) worker = EnvironmentWorker(client, workdir=".") # Nothing supplied at all -> the first missing one (work_id) is named. with pytest.raises(ValueError, match=r"handle_item: work_id is required — pass it or set ANTHROPIC_WORK_ID"): await worker.handle_item() # environment_key still missing even though the others are supplied. with pytest.raises( ValueError, match=r"handle_item: environment_key is required — pass it or set ANTHROPIC_ENVIRONMENT_KEY" ): await worker.handle_item(work_id="w_1", environment_id="e_1", session_id="s_1") @pytest.mark.skipif(PYDANTIC_V1, reason="tool functions are only supported with pydantic v2") @pytest.mark.asyncio() async def test_worker_threads_extra_headers_into_poll_heartbeat_stop_and_runner( monkeypatch: pytest.MonkeyPatch, ) -> None: """A worker-level ``extra_headers`` is threaded, unchanged, into every per-request call the worker drives: the poll loop (forwarded to ``aiter_work``), the lease heartbeat, the force-stop, and the session tool runner. The worker does no header munging — it passes the caller's mapping through to each call's ``extra_headers=``. Auth stays on the scoped sub-clients the worker builds (``env_key`` Bearer), independent of this passthrough. """ work = _FakeWorkResource(heartbeat_state="running") sessions = _FakeSessions() client = _fake_client(work, sessions) scoped_calls = _install_scoped_client(monkeypatch, work, sessions) aiter_kwargs: dict[str, Any] = {} async def fake_aiter_work(_work: Any, **kw: Any) -> AsyncIterator[Any]: aiter_kwargs.update(kw) yield _work_item() monkeypatch.setattr(worker_mod, "aiter_work", fake_aiter_work) record: dict[str, Any] = {} _install_run_session_tools(monkeypatch, record) extras = {"x-trace-id": "abc123"} worker = EnvironmentWorker( client=client, environment_id="e_1", environment_key="env_key", workdir=".", extra_headers=extras, ) await asyncio.wait_for(worker.run(), timeout=5) # The poll loop, heartbeat and force-stop each receive the caller's # passthrough mapping unchanged. assert aiter_kwargs["extra_headers"] == extras assert work.heartbeat_calls[0]["extra_headers"] == extras assert work.stop_calls[0]["extra_headers"] == extras # The session tool runner is handed the same passthrough mapping (and the # environment key, which it uses to build its own scoped sub-client). assert record["run"]["extra_headers"] == extras assert record["run"]["environment_key"] == "env_key" # Two scoped sub-clients are constructed per run() pass: one for the # poller, one for the worker (heartbeat / force-stop). Both use the same # environment key as their Bearer credential. assert scoped_calls == [ {"auth_token": "env_key", "helper": "environments-work-poller"}, {"auth_token": "env_key", "helper": "environments-worker"}, ] def test_work_resource_worker_builds_environment_worker() -> None: """``client.beta.environments.work.worker(...)`` builds an ``EnvironmentWorker`` bound to the client, with the options threaded through (mirrors ``poller``).""" client = AsyncAnthropic(api_key="x") worker = client.beta.environments.work.worker( environment_id="e_1", environment_key="env_key", workdir="/workspace", unrestricted_paths=True, max_idle=12.0, worker_id="w-test", extra_headers={"x-trace-id": "abc123"}, ) assert isinstance(worker, EnvironmentWorker) assert worker._client is client assert worker._environment_id == "e_1" assert worker._environment_key == "env_key" assert worker._workdir == "/workspace" assert worker._unrestricted_paths is True assert worker._max_idle == 12.0 assert worker._worker_id == "w-test" assert worker._extra_headers == {"x-trace-id": "abc123"} def test_work_resource_worker_defaults() -> None: worker = AsyncAnthropic(api_key="x").beta.environments.work.worker() assert isinstance(worker, EnvironmentWorker) assert worker._environment_id is None assert worker._environment_key is None assert worker._tools is None # Default workdir is the cwd snapshotted at construction (not a lazily # resolved ".") — TS parity with process.cwd()-at-construction. assert worker._workdir == os.getcwd() assert worker._unrestricted_paths is False assert worker._max_idle == 60.0 def test_work_resource_worker_and_poller_async_only() -> None: """``worker()`` / ``poller()`` build an async-only ``EnvironmentWorker`` / ``aiter_work`` generator, so they live on ``AsyncWork`` and are NOT exposed on the sync ``Work`` resource (calling them from the sync client would hand back coroutines/async iterators that can't run without an event loop).""" async_work = AsyncAnthropic(api_key="x").beta.environments.work assert hasattr(async_work, "worker") assert hasattr(async_work, "poller") sync_work = Anthropic(api_key="x").beta.environments.work assert not hasattr(sync_work, "worker") assert not hasattr(sync_work, "poller") @pytest.mark.skipif(PYDANTIC_V1, reason="tool functions are only supported with pydantic v2") @pytest.mark.asyncio() async def test_heartbeat_starts_before_skill_download(monkeypatch: pytest.MonkeyPatch) -> None: """Regression: the lease heartbeat must already be running while skills are downloaded. Skill setup (``AgentToolContext.__aenter__``) can take longer than the lease TTL. If the first heartbeat only fired *after* that download (the old ordering) the lease could lapse mid-download and another worker would reclaim the item — both then serve the same session (split-brain). We make skill setup block until a heartbeat has fired: with the correct ordering it proceeds; with the old ordering it would hang and time out. """ import anthropic.lib.tools.agent_toolset as ats heartbeat_fired = asyncio.Event() order: list[str] = [] class _HeartbeatFirstWork(_FakeWorkResource): @override async def heartbeat( self, work_id: str, # noqa: ARG002 *, environment_id: str, # noqa: ARG002 expected_last_heartbeat: str, # noqa: ARG002 extra_headers: Any = None, # noqa: ARG002 ) -> Any: order.append("heartbeat") heartbeat_fired.set() # state="running" so the heartbeat loop keeps going (does not stop # the run before skill setup / the session runner get to execute). return SimpleNamespace(last_heartbeat="hb-1", ttl_seconds=60, state="running", lease_extended=True) work = _HeartbeatFirstWork() sessions = _FakeSessions() client = _fake_client(work, sessions) _install_aiter_work(monkeypatch, [_work_item()]) _install_scoped_client(monkeypatch, work, sessions) async def slow_setup_skills(_self: Any) -> None: order.append("setup_start") # If the heartbeat hasn't started yet (old bug) this hangs → timeout. await asyncio.wait_for(heartbeat_fired.wait(), timeout=5) order.append("setup_end") monkeypatch.setattr(ats.AgentToolContext, "setup_skills", slow_setup_skills) @contextlib.asynccontextmanager async def fake_run_session_tools( _client: Any, session_id: str, # noqa: ARG001 *, tools: Any, # noqa: ARG001 max_idle: Any = None, # noqa: ARG001 environment_key: Any = None, # noqa: ARG001 extra_headers: Any = None, # noqa: ARG001 ): async def _iter() -> AsyncIterator[Any]: return yield # pragma: no cover (makes this an async generator function) yield _iter() monkeypatch.setattr(worker_mod, "_run_session_tools", fake_run_session_tools) worker = EnvironmentWorker(client=client, environment_id="e_1", environment_key="env_key", workdir=".") await asyncio.wait_for(worker.run(), timeout=5) assert "heartbeat" in order assert "setup_end" in order # The heartbeat fired before skill setup was allowed to finish. assert order.index("heartbeat") < order.index("setup_end") # The work item was still force-stopped on exit. assert len(work.stop_calls) == 1 anthropic-sdk-python-0.120.2/tests/lib/sessions/000077500000000000000000000000001523216435200215225ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/sessions/__init__.py000066400000000000000000000000001523216435200236210ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/sessions/test_accumulate.py000066400000000000000000000135341523216435200252640ustar00rootroot00000000000000from __future__ import annotations from typing import Any, cast from datetime import datetime, timezone import pytest from anthropic import AnthropicError from anthropic._models import build from anthropic.types.beta import ( BetaManagedAgentsDeltaEvent, BetaManagedAgentsStartEvent, BetaManagedAgentsDeltaContent, BetaManagedAgentsAgentMessagePreview, BetaManagedAgentsAgentThinkingPreview, ) from anthropic.lib.sessions import accumulate_managed_agents_event from anthropic.types.beta.sessions import BetaManagedAgentsTextBlock, BetaManagedAgentsAgentMessageEvent def start(event_id: str) -> BetaManagedAgentsStartEvent: return BetaManagedAgentsStartEvent( type="event_start", event=BetaManagedAgentsAgentMessagePreview(id=event_id, type="agent.message"), ) def seed(event_id: str) -> BetaManagedAgentsAgentMessageEvent: msg = accumulate_managed_agents_event(None, start(event_id)) assert msg is not None, "expected agent.message seed" return msg def fold( msg: BetaManagedAgentsAgentMessageEvent, ev: BetaManagedAgentsDeltaEvent, ) -> BetaManagedAgentsAgentMessageEvent: next_ = accumulate_managed_agents_event(msg, ev) assert next_ is not None, "expected snapshot after delta" return next_ def delta(event_id: str, text: str, index: int | None = None) -> BetaManagedAgentsDeltaEvent: return BetaManagedAgentsDeltaEvent( type="event_delta", event_id=event_id, delta=build( BetaManagedAgentsDeltaContent, type="content_delta", index=index, content=BetaManagedAgentsTextBlock(type="text", text=text), ), ) def test_event_start_returns_a_fresh_empty_snapshot_from_none() -> None: msg = accumulate_managed_agents_event(None, start("evt_1")) assert msg is not None assert msg.id == "evt_1" assert msg.type == "agent.message" assert msg.content == [] def test_event_start_for_a_non_agent_message_preview_returns_none() -> None: ev = BetaManagedAgentsStartEvent( type="event_start", event=BetaManagedAgentsAgentThinkingPreview(id="evt_1", type="agent.thinking"), ) assert accumulate_managed_agents_event(None, ev) is None def test_new_index_inserts_the_fragment_as_a_fresh_block() -> None: msg = seed("evt_1") next_ = fold(msg, delta("evt_1", "Hello", 0)) assert next_.content == [BetaManagedAgentsTextBlock(type="text", text="Hello")] def test_existing_text_index_appends() -> None: msg = seed("evt_1") msg = fold(msg, delta("evt_1", "Hel", 0)) msg = fold(msg, delta("evt_1", "lo", 0)) msg = fold(msg, delta("evt_1", "World", 1)) assert msg.content == [ BetaManagedAgentsTextBlock(type="text", text="Hello"), BetaManagedAgentsTextBlock(type="text", text="World"), ] def test_defaults_index_to_0() -> None: msg = seed("evt_1") msg = fold(msg, delta("evt_1", "a")) msg = fold(msg, delta("evt_1", "b")) assert msg.content == [BetaManagedAgentsTextBlock(type="text", text="ab")] def test_throws_on_an_index_gap() -> None: msg = seed("evt_1") with pytest.raises(AnthropicError, match=r"event_delta index 2 is beyond the end of content \(length 0\)"): accumulate_managed_agents_event(msg, delta("evt_1", "x", 2)) def test_throws_on_event_delta_with_no_prior_snapshot() -> None: with pytest.raises(AnthropicError, match=r"event_delta for evt_1 received before its event_start"): accumulate_managed_agents_event(None, delta("evt_1", "x", 0)) def test_next_sequential_index_inserts() -> None: msg = seed("evt_1") msg = fold(msg, delta("evt_1", "a", 0)) msg = fold(msg, delta("evt_1", "b", 1)) assert msg.content == [ BetaManagedAgentsTextBlock(type="text", text="a"), BetaManagedAgentsTextBlock(type="text", text="b"), ] def test_returns_a_new_snapshot_and_does_not_mutate_the_input() -> None: msg = seed("evt_1") next_ = fold(msg, delta("evt_1", "x", 0)) assert next_ is not msg assert next_.content is not msg.content assert msg.content == [] after = fold(next_, delta("evt_1", "y", 0)) assert after.content[0] is not next_.content[0] assert next_.content == [BetaManagedAgentsTextBlock(type="text", text="x")] def test_does_not_mutate_the_wire_delta_when_inserting_at_a_new_index() -> None: d = delta("evt_1", "x", 0) msg = seed("evt_1") msg = fold(msg, d) msg = fold(msg, delta("evt_1", "y", 0)) assert d.delta.content.text == "x" def test_agent_message_replaces_the_preview_with_a_copy_of_the_final_event() -> None: msg = seed("evt_1") msg = fold(msg, delta("evt_1", "partial", 0)) final = BetaManagedAgentsAgentMessageEvent( id="evt_1", type="agent.message", content=[BetaManagedAgentsTextBlock(type="text", text="complete")], processed_at=datetime(2024, 1, 1, tzinfo=timezone.utc), ) result = accumulate_managed_agents_event(msg, final) assert result == final assert result is not final assert result.content is not final.content assert result.content[0] is not final.content[0] def test_agent_message_accepts_none_snapshot() -> None: final = BetaManagedAgentsAgentMessageEvent( id="evt_1", type="agent.message", content=[BetaManagedAgentsTextBlock(type="text", text="complete")], processed_at=datetime(2024, 1, 1, tzinfo=timezone.utc), ) assert accumulate_managed_agents_event(None, final) == final def test_unknown_block_type_pair_is_a_noop_forward_compat() -> None: msg = seed("evt_1") # Existing block of a future, non-text type: future_block = BetaManagedAgentsTextBlock.model_construct(type="tool_use", id="t", name="n", input={}) before = future_block.model_dump() msg.content.append(cast(Any, future_block)) next_ = fold(msg, delta("evt_1", "ignored", 0)) assert next_.content[0].model_dump() == before anthropic-sdk-python-0.120.2/tests/lib/streaming/000077500000000000000000000000001523216435200216455ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/streaming/__init__.py000066400000000000000000000000001523216435200237440ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/streaming/fixtures/000077500000000000000000000000001523216435200235165ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/streaming/fixtures/basic_response.txt000066400000000000000000000020261523216435200272560ustar00rootroot00000000000000event: message_start data: {"type":"message_start","message":{"id":"msg_4QpJur2dWWDjF6C758FbBw5vm12BaVipnK","type":"message","role":"assistant","content":[],"model":"claude-3-opus-latest","stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":11,"output_tokens":1}}} event: content_block_start data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}} event: ping data: {"type": "ping"} event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello"}} event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" there"}} event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"!"}} event: content_block_stop data: {"type":"content_block_stop","index":0} event: message_delta data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"output_tokens":6}} event: message_stop data: {"type":"message_stop"}anthropic-sdk-python-0.120.2/tests/lib/streaming/fixtures/compaction_response.txt000066400000000000000000000023551523216435200303360ustar00rootroot00000000000000event: message_start data: {"type":"message_start","message":{"id":"msg_01CompactionEncryptedContent01","type":"message","role":"assistant","content":[],"model":"claude-opus-4-7","stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":30,"output_tokens":1}}} event: content_block_start data: {"type":"content_block_start","index":0,"content_block":{"type":"compaction","content":null,"encrypted_content":null}} event: ping data: {"type": "ping"} event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"compaction_delta","content":"Earlier conversation summarized.","encrypted_content":"EpwBCioIDxgCEAEYASJALd_opaque_compaction_payload"}} event: content_block_stop data: {"type":"content_block_stop","index":0} event: content_block_start data: {"type":"content_block_start","index":1,"content_block":{"type":"text","text":""}} event: content_block_delta data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"Hello there!"}} event: content_block_stop data: {"type":"content_block_stop","index":1} event: message_delta data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"output_tokens":8}} event: message_stop data: {"type":"message_stop"} anthropic-sdk-python-0.120.2/tests/lib/streaming/fixtures/fallback_credit_response.txt000066400000000000000000000014651523216435200312740ustar00rootroot00000000000000event: message_start data: {"type":"message_start","message":{"id":"msg_01FallbackCreditUsage0000001","type":"message","role":"assistant","content":[],"model":"claude-sonnet-4-5","stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":25,"output_tokens":1}}} event: content_block_start data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}} event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello there!"}} event: content_block_stop data: {"type":"content_block_stop","index":0} event: message_delta data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"output_tokens":8,"fallback_credit":{"status":{"type":"redeemed"}}}} event: message_stop data: {"type":"message_stop"} anthropic-sdk-python-0.120.2/tests/lib/streaming/fixtures/fallback_response.txt000066400000000000000000000020551523216435200277360ustar00rootroot00000000000000event: message_start data: {"type":"message_start","message":{"id":"msg_01FallbackModelRelabel000001","type":"message","role":"assistant","content":[],"model":"claude-opus-4-7","stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":25,"output_tokens":1}}} event: content_block_start data: {"type":"content_block_start","index":0,"content_block":{"type":"fallback","from":{"model":"claude-opus-4-7"},"to":{"model":"claude-sonnet-4-5"},"trigger":{"type":"refusal","category":null}}} event: content_block_stop data: {"type":"content_block_stop","index":0} event: content_block_start data: {"type":"content_block_start","index":1,"content_block":{"type":"text","text":""}} event: content_block_delta data: {"type":"content_block_delta","index":1,"delta":{"type":"text_delta","text":"Hello there!"}} event: content_block_stop data: {"type":"content_block_stop","index":1} event: message_delta data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"output_tokens":8}} event: message_stop data: {"type":"message_stop"} anthropic-sdk-python-0.120.2/tests/lib/streaming/fixtures/incomplete_partial_json_response.txt000066400000000000000000000046201523216435200331030ustar00rootroot00000000000000event: message_start data: {"type":"message_start","message":{"id":"msg_01UdjYBBipA9omjYhicnevgq","type":"message","role":"assistant","model":"claude-3-7-sonnet-20250219","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":450,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":1,"service_tier":"standard"}} } event: content_block_start data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""} } event: ping data: {"type": "ping"} event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"I"} } event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"'ll create a comprehensive tax guide for"} } event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" someone with multiple W2s an"} } event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"d save it in a file called taxes.txt. Let"} } event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" me do that for you now."} } event: content_block_stop data: {"type":"content_block_stop","index":0 } event: content_block_start data: {"type":"content_block_start","index":1,"content_block":{"type":"tool_use","id":"toolu_01EKqbqmZrGRXy18eN7m9kvY","name":"make_file","input":{}} } event: content_block_delta data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":""} } event: content_block_delta data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"{\"filename\": \"taxes.txt"} } event: content_block_delta data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"\", \"lines_of_text\": [\n\"# COMPREHENSIVE TAX GUIDE FOR INDIVIDUALS WITH MULTIPLE W-2s\",\n\"\",\n\"## INTRODUCTION\",\n\"\","} } event: content_block_delta data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"\n\"Filing taxes"} } event: message_delta data: {"type":"message_delta","delta":{"stop_reason":"max_tokens","stop_sequence":null},"usage":{"output_tokens":124} } event: message_stop data: {"type":"message_stop" }anthropic-sdk-python-0.120.2/tests/lib/streaming/fixtures/refusal_response.txt000066400000000000000000000013601523216435200276360ustar00rootroot00000000000000event: message_start data: {"type":"message_start","message":{"id":"msg_01RefusalTestMessage123456789","type":"message","role":"assistant","content":[],"model":"claude-opus-4-7","stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":20,"output_tokens":1}}} event: content_block_start data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}} event: content_block_stop data: {"type":"content_block_stop","index":0} event: message_delta data: {"type":"message_delta","delta":{"stop_reason":"refusal","stop_sequence":null,"stop_details":{"type":"refusal","category":"cyber","explanation":"This request was refused due to policy."}},"usage":{"output_tokens":0}} event: message_stop data: {"type":"message_stop"} anthropic-sdk-python-0.120.2/tests/lib/streaming/fixtures/tool_use_response.txt000066400000000000000000000037201523216435200300300ustar00rootroot00000000000000event: message_start data: {"type":"message_start","message":{"id":"msg_019Q1hrJbZG26Fb9BQhrkHEr","type":"message","role":"assistant","model":"claude-sonnet-4-20250514","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":377,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":1,"service_tier":"standard"}}} event: content_block_start data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}} event: ping data: {"type": "ping"} event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"I"}} event: content_block_delta data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"'ll check the current weather in Paris for you."}} event: content_block_stop data: {"type":"content_block_stop","index":0} event: content_block_start data: {"type":"content_block_start","index":1,"content_block":{"type":"tool_use","id":"toolu_01NRLabsLyVHZPKxbKvkfSMn","name":"get_weather","caller":{"type":"direct"},"input":{}}} event: content_block_delta data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":""}} event: content_block_delta data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"{\"locati"}} event: content_block_delta data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"on\": \"P"}} event: content_block_delta data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"ar"}} event: content_block_delta data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"is\"}"}} event: content_block_stop data: {"type":"content_block_stop","index":1} event: message_delta data: {"type":"message_delta","delta":{"stop_reason":"tool_use","stop_sequence":null},"usage":{"output_tokens":65}} event: message_stop data: {"type":"message_stop"}anthropic-sdk-python-0.120.2/tests/lib/streaming/helpers.py000066400000000000000000000015571523216435200236710ustar00rootroot00000000000000from __future__ import annotations import os from typing import TypeVar, Iterator from typing_extensions import AsyncIterator _T = TypeVar("_T") def load_fixture(fixture_name: str) -> str: """Load a fixture file from the fixtures directory.""" current_dir = os.path.dirname(os.path.abspath(__file__)) fixtures_dir = os.path.join(current_dir, "fixtures") with open(os.path.join(fixtures_dir, fixture_name), "r") as f: return f.read() def get_response(fixture_name: str) -> Iterator[bytes]: """Convert a fixture file into a stream of bytes for testing.""" content = load_fixture(fixture_name) for line in content.splitlines(): yield line.encode() + b"\n" async def to_async_iter(iter: Iterator[_T]) -> AsyncIterator[_T]: """Convert a synchronous iterator to an asynchronous one.""" for event in iter: yield event anthropic-sdk-python-0.120.2/tests/lib/streaming/test_beta_messages.py000066400000000000000000000550501523216435200260650ustar00rootroot00000000000000from __future__ import annotations import os import json from typing import Any, Set, Dict, TypeVar, cast from unittest import TestCase import httpx import pytest from respx import MockRouter from anthropic import Anthropic, AsyncAnthropic from anthropic._utils import assert_overloads_in_sync, assert_signatures_in_sync from anthropic._compat import PYDANTIC_V1 from anthropic.types.beta.beta_message import BetaMessage from anthropic.lib.streaming._beta_types import BetaCompactionEvent, ParsedBetaMessageStreamEvent from anthropic.resources.messages.messages import DEPRECATED_MODELS from anthropic.lib.streaming._beta_messages import TRACKS_TOOL_INPUT, BetaMessageStream, BetaAsyncMessageStream from .helpers import get_response, to_async_iter base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") api_key = "my-anthropic-api-key" sync_client = Anthropic(base_url=base_url, api_key=api_key, _strict_response_validation=True) async_client = AsyncAnthropic(base_url=base_url, api_key=api_key, _strict_response_validation=True) _T = TypeVar("_T") # Expected message fixtures EXPECTED_BASIC_MESSAGE = { "id": "msg_4QpJur2dWWDjF6C758FbBw5vm12BaVipnK", "model": "claude-3-opus-latest", "role": "assistant", "stop_reason": "end_turn", "type": "message", "content": [{"type": "text", "text": "Hello there!"}], "usage": {"input_tokens": 11, "output_tokens": 6}, } EXPECTED_BASIC_EVENT_TYPES = [ "message_start", "content_block_start", "content_block_delta", "text", "content_block_delta", "text", "content_block_delta", "text", "content_block_stop", "message_delta", ] EXPECTED_TOOL_USE_MESSAGE = { "id": "msg_019Q1hrJbZG26Fb9BQhrkHEr", "model": "claude-sonnet-4-20250514", "role": "assistant", "stop_reason": "tool_use", "type": "message", "content": [ {"type": "text", "text": "I'll check the current weather in Paris for you."}, { "type": "tool_use", "caller": {"type": "direct"}, "id": "toolu_01NRLabsLyVHZPKxbKvkfSMn", "name": "get_weather", "input": {"location": "Paris"}, }, ], "usage": { "input_tokens": 377, "output_tokens": 65, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "service_tier": "standard", }, } EXPECTED_TOOL_USE_EVENT_TYPES = [ "message_start", "content_block_start", "content_block_delta", "text", "content_block_delta", "text", "content_block_stop", "content_block_start", "content_block_delta", "input_json", "content_block_delta", "input_json", "content_block_delta", "input_json", "content_block_delta", "input_json", "content_block_delta", "input_json", "content_block_stop", "message_delta", ] EXPECTED_INCOMPLETE_MESSAGE = { "id": "msg_01UdjYBBipA9omjYhicnevgq", "model": "claude-3-7-sonnet-20250219", "role": "assistant", "stop_reason": "max_tokens", "type": "message", "content": [ { "type": "text", "text": "I'll create a comprehensive tax guide for someone with multiple W2s and save it in a file called taxes.txt. Let me do that for you now.", }, { "type": "tool_use", "id": "toolu_01EKqbqmZrGRXy18eN7m9kvY", "name": "make_file", "input": { "filename": "taxes.txt", "lines_of_text": [ "# COMPREHENSIVE TAX GUIDE FOR INDIVIDUALS WITH MULTIPLE W-2s", "", "## INTRODUCTION", "", ], }, }, ], "usage": { "input_tokens": 450, "output_tokens": 124, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "service_tier": "standard", }, } EXPECTED_INCOMPLETE_EVENT_TYPES = [ "message_start", "content_block_start", "content_block_delta", "text", "content_block_delta", "text", "content_block_delta", "text", "content_block_delta", "text", "content_block_delta", "text", "content_block_stop", "content_block_start", "content_block_delta", "input_json", "content_block_delta", "input_json", "content_block_delta", "input_json", "content_block_delta", "input_json", "message_delta", ] EXPECTED_COMPACTION_MESSAGE = { "id": "msg_01CompactionEncryptedContent01", "model": "claude-opus-4-7", "role": "assistant", "stop_reason": "end_turn", "type": "message", "content": [ { "type": "compaction", "content": "Earlier conversation summarized.", "encrypted_content": "EpwBCioIDxgCEAEYASJALd_opaque_compaction_payload", }, {"type": "text", "text": "Hello there!"}, ], "usage": {"input_tokens": 30, "output_tokens": 8}, } EXPECTED_COMPACTION_EVENT_TYPES = [ "message_start", "content_block_start", "content_block_delta", "compaction", "content_block_stop", "content_block_start", "content_block_delta", "text", "content_block_stop", "message_delta", ] def assert_message_matches(message: BetaMessage, expected: Dict[str, Any]) -> None: actual_message_json = message.model_dump_json( indent=2, exclude_none=True, exclude={"content": {"__all__": {"__json_buf"}}} ) test_case = TestCase() test_case.maxDiff = None test_case.assertEqual(expected, json.loads(actual_message_json)) def assert_basic_response(events: list[ParsedBetaMessageStreamEvent], message: BetaMessage) -> None: assert_message_matches(message, EXPECTED_BASIC_MESSAGE) assert [e.type for e in events] == EXPECTED_BASIC_EVENT_TYPES def assert_tool_use_response(events: list[ParsedBetaMessageStreamEvent], message: BetaMessage) -> None: assert_message_matches(message, EXPECTED_TOOL_USE_MESSAGE) assert [e.type for e in events] == EXPECTED_TOOL_USE_EVENT_TYPES def assert_incomplete_partial_input_response(events: list[ParsedBetaMessageStreamEvent], message: BetaMessage) -> None: assert_message_matches(message, EXPECTED_INCOMPLETE_MESSAGE) assert [e.type for e in events] == EXPECTED_INCOMPLETE_EVENT_TYPES def assert_compaction_response(events: list[ParsedBetaMessageStreamEvent], message: BetaMessage) -> None: assert_message_matches(message, EXPECTED_COMPACTION_MESSAGE) assert [e.type for e in events] == EXPECTED_COMPACTION_EVENT_TYPES # the emitted compaction event must carry encrypted_content, not just the accumulated block compaction_events = [e for e in events if isinstance(e, BetaCompactionEvent)] assert len(compaction_events) == 1 assert compaction_events[0].encrypted_content == "EpwBCioIDxgCEAEYASJALd_opaque_compaction_payload" def assert_refusal_response(message: BetaMessage) -> None: assert message.stop_reason == "refusal" assert message.stop_details is not None assert message.stop_details.type == "refusal" assert message.stop_details.category == "cyber" assert message.stop_details.explanation == "This request was refused due to policy." EXPECTED_FALLBACK_EVENT_TYPES = [ "message_start", "content_block_start", "content_block_stop", "content_block_start", "content_block_delta", "text", "content_block_stop", "message_delta", ] def assert_fallback_response(events: list[ParsedBetaMessageStreamEvent], message: BetaMessage) -> None: assert [e.type for e in events] == EXPECTED_FALLBACK_EVENT_TYPES # `message_start` carried the declined model; the accumulated message must # be relabeled to the serving model from the fallback block assert message.model == "claude-sonnet-4-5" assert message.content[0].type == "fallback" text_block = message.content[1] assert text_block.type == "text" assert text_block.text == "Hello there!" def assert_fallback_credit_response(message: BetaMessage) -> None: # `message_delta` carried `usage.fallback_credit`; the accumulated final # message must surface it rather than dropping it assert message.usage.fallback_credit is not None assert message.usage.fallback_credit.status.type == "redeemed" assert message.usage.output_tokens == 8 class TestSyncMessages: @pytest.mark.respx(base_url=base_url) def test_basic_response(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, content=get_response("basic_response.txt")) ) with sync_client.beta.messages.stream( max_tokens=1024, messages=[ { "role": "user", "content": "Say hello there!", } ], model="claude-3-opus-latest", ) as stream: assert isinstance(cast(Any, stream), BetaMessageStream) assert_basic_response([event for event in stream], stream.get_final_message()) @pytest.mark.respx(base_url=base_url) def test_tool_use(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, content=get_response("tool_use_response.txt")) ) with sync_client.beta.messages.stream( max_tokens=1024, messages=[ { "role": "user", "content": "Say hello there!", } ], model="claude-sonnet-4-5", ) as stream: assert isinstance(cast(Any, stream), BetaMessageStream) assert_tool_use_response([event for event in stream], stream.get_final_message()) @pytest.mark.respx(base_url=base_url) def test_context_manager(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, content=get_response("basic_response.txt")) ) with sync_client.beta.messages.stream( max_tokens=1024, messages=[ { "role": "user", "content": "Say hello there!", } ], model="claude-3-opus-latest", ) as stream: assert not stream.response.is_closed # response should be closed even if the body isn't read assert stream.response.is_closed @pytest.mark.respx(base_url=base_url) def test_deprecated_model_warning_stream(self, respx_mock: MockRouter) -> None: for deprecated_model in DEPRECATED_MODELS: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, content=get_response("basic_response.txt")) ) with pytest.warns(DeprecationWarning, match=f"The model '{deprecated_model}' is deprecated"): with sync_client.beta.messages.stream( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model=deprecated_model, ) as stream: # Consume the stream to ensure the warning is triggered stream.until_done() @pytest.mark.respx(base_url=base_url) def test_refusal_stop_details_propagated(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, content=get_response("refusal_response.txt")) ) with sync_client.beta.messages.stream( max_tokens=1024, messages=[{"role": "user", "content": "Say hello there!"}], model="claude-opus-4-7", ) as stream: assert_refusal_response(stream.get_final_message()) @pytest.mark.respx(base_url=base_url) def test_compaction(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, content=get_response("compaction_response.txt")) ) with sync_client.beta.messages.stream( max_tokens=1024, messages=[{"role": "user", "content": "Say hello there!"}], model="claude-opus-4-7", ) as stream: assert isinstance(cast(Any, stream), BetaMessageStream) assert_compaction_response([event for event in stream], stream.get_final_message()) @pytest.mark.respx(base_url=base_url) def test_fallback_relabels_model(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, content=get_response("fallback_response.txt")) ) with sync_client.beta.messages.stream( max_tokens=1024, messages=[{"role": "user", "content": "Say hello there!"}], model="claude-opus-4-7", ) as stream: assert isinstance(cast(Any, stream), BetaMessageStream) assert_fallback_response([event for event in stream], stream.get_final_message()) @pytest.mark.respx(base_url=base_url) def test_fallback_credit_usage_propagated(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, content=get_response("fallback_credit_response.txt")) ) with sync_client.beta.messages.stream( max_tokens=1024, messages=[{"role": "user", "content": "Say hello there!"}], model="claude-sonnet-4-5", ) as stream: assert_fallback_credit_response(stream.get_final_message()) class TestAsyncMessages: @pytest.mark.asyncio @pytest.mark.respx(base_url=base_url) async def test_basic_response(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, content=to_async_iter(get_response("basic_response.txt"))) ) async with async_client.beta.messages.stream( max_tokens=1024, messages=[ { "role": "user", "content": "Say hello there!", } ], model="claude-opus-4-5", ) as stream: assert isinstance(cast(Any, stream), BetaAsyncMessageStream) assert_basic_response([event async for event in stream], await stream.get_final_message()) @pytest.mark.asyncio @pytest.mark.respx(base_url=base_url) async def test_context_manager(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, content=to_async_iter(get_response("basic_response.txt"))) ) async with async_client.beta.messages.stream( max_tokens=1024, messages=[ { "role": "user", "content": "Say hello there!", } ], model="claude-3-opus-latest", ) as stream: assert not stream.response.is_closed # response should be closed even if the body isn't read assert stream.response.is_closed @pytest.mark.asyncio @pytest.mark.respx(base_url=base_url) async def test_deprecated_model_warning_stream(self, respx_mock: MockRouter) -> None: for deprecated_model in DEPRECATED_MODELS: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, content=to_async_iter(get_response("basic_response.txt"))) ) with pytest.warns(DeprecationWarning, match=f"The model '{deprecated_model}' is deprecated"): async with async_client.beta.messages.stream( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model=deprecated_model, ) as stream: # Consume the stream to ensure the warning is triggered await stream.get_final_message() @pytest.mark.asyncio @pytest.mark.respx(base_url=base_url) async def test_tool_use(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, content=to_async_iter(get_response("tool_use_response.txt"))) ) async with async_client.beta.messages.stream( max_tokens=1024, messages=[ { "role": "user", "content": "Say hello there!", } ], model="claude-sonnet-4-5", ) as stream: assert isinstance(cast(Any, stream), BetaAsyncMessageStream) assert_tool_use_response([event async for event in stream], await stream.get_final_message()) @pytest.mark.asyncio @pytest.mark.respx(base_url=base_url) async def test_incomplete_response(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response( 200, content=to_async_iter(get_response("incomplete_partial_json_response.txt")) ) ) async with async_client.beta.messages.stream( max_tokens=1024, messages=[ { "role": "user", "content": "Say hello there!", } ], model="claude-sonnet-4-5", ) as stream: assert isinstance(cast(Any, stream), BetaAsyncMessageStream) assert_incomplete_partial_input_response( [event async for event in stream], await stream.get_final_message() ) @pytest.mark.asyncio @pytest.mark.respx(base_url=base_url) async def test_refusal_stop_details_propagated(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, content=to_async_iter(get_response("refusal_response.txt"))) ) async with async_client.beta.messages.stream( max_tokens=1024, messages=[{"role": "user", "content": "Say hello there!"}], model="claude-opus-4-7", ) as stream: assert_refusal_response(await stream.get_final_message()) @pytest.mark.asyncio @pytest.mark.respx(base_url=base_url) async def test_compaction(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, content=to_async_iter(get_response("compaction_response.txt"))) ) async with async_client.beta.messages.stream( max_tokens=1024, messages=[{"role": "user", "content": "Say hello there!"}], model="claude-opus-4-7", ) as stream: assert isinstance(cast(Any, stream), BetaAsyncMessageStream) assert_compaction_response([event async for event in stream], await stream.get_final_message()) @pytest.mark.asyncio @pytest.mark.respx(base_url=base_url) async def test_fallback_relabels_model(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, content=to_async_iter(get_response("fallback_response.txt"))) ) async with async_client.beta.messages.stream( max_tokens=1024, messages=[{"role": "user", "content": "Say hello there!"}], model="claude-opus-4-7", ) as stream: assert isinstance(cast(Any, stream), BetaAsyncMessageStream) assert_fallback_response([event async for event in stream], await stream.get_final_message()) @pytest.mark.asyncio @pytest.mark.respx(base_url=base_url) async def test_fallback_credit_usage_propagated(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, content=to_async_iter(get_response("fallback_credit_response.txt"))) ) async with async_client.beta.messages.stream( max_tokens=1024, messages=[{"role": "user", "content": "Say hello there!"}], model="claude-sonnet-4-5", ) as stream: assert_fallback_credit_response(await stream.get_final_message()) @pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) def test_stream_method_definition_in_sync(sync: bool) -> None: client: Anthropic | AsyncAnthropic = sync_client if sync else async_client assert_signatures_in_sync( client.beta.messages.create, client.beta.messages.stream, exclude_params={"stream", "output_format"}, ) @pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) def test_parse_method_definition_in_sync(sync: bool) -> None: client: Anthropic | AsyncAnthropic = sync_client if sync else async_client assert_signatures_in_sync( client.beta.messages.create, client.beta.messages.parse, exclude_params={"stream", "output_format"}, ) @pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) def test_tool_runner_method_definition_in_sync(sync: bool) -> None: client: Anthropic | AsyncAnthropic = sync_client if sync else async_client assert_overloads_in_sync( client.beta.messages.create, client.beta.messages.tool_runner, exclude_params={"stream", "tools", "max_iterations", "compaction_control", "output_format"}, ) # go through all the ContentBlock types to make sure the type alias is up to date # with any type that has an input property of type object def test_tracks_tool_input_type_alias_is_up_to_date() -> None: # only run this on Pydantic v2 if PYDANTIC_V1: pytest.skip("This test is only applicable for Pydantic v2") from typing import get_args from pydantic import BaseModel from anthropic.types.beta.beta_content_block import BetaContentBlock # Get the content block union type content_block_union = get_args(BetaContentBlock)[0] # Get all types from BetaContentBlock union content_block_types = get_args(content_block_union) # Types that should have an input property types_with_input: Set[Any] = set() # Check each type to see if it has an input property in its model_fields for block_type in content_block_types: if issubclass(block_type, BaseModel) and "input" in block_type.model_fields: types_with_input.add(block_type) # Get the types included in TRACKS_TOOL_INPUT tracked_types = TRACKS_TOOL_INPUT # Make sure all types with input are tracked for block_type in types_with_input: assert block_type in tracked_types, ( f"ContentBlock type {block_type.__name__} has an input property, " f"but is not included in TRACKS_TOOL_INPUT. You probably need to update the TRACKS_TOOL_INPUT type alias." ) anthropic-sdk-python-0.120.2/tests/lib/streaming/test_messages.py000066400000000000000000000312041523216435200250650ustar00rootroot00000000000000from __future__ import annotations import os from typing import Any, Set, TypeVar, cast import httpx import pytest from respx import MockRouter from anthropic import Stream, Anthropic, AsyncStream, AsyncAnthropic from anthropic._utils import assert_signatures_in_sync from anthropic._compat import PYDANTIC_V1 from anthropic.lib.streaming import ParsedMessageStreamEvent from anthropic.types.message import Message from anthropic.resources.messages import DEPRECATED_MODELS from anthropic.lib.streaming._messages import TRACKS_TOOL_INPUT from .helpers import get_response, to_async_iter base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") api_key = "my-anthropic-api-key" sync_client = Anthropic(base_url=base_url, api_key=api_key, _strict_response_validation=True) async_client = AsyncAnthropic(base_url=base_url, api_key=api_key, _strict_response_validation=True) _T = TypeVar("_T") def assert_basic_response(events: list[ParsedMessageStreamEvent[None]], message: Message) -> None: assert message.id == "msg_4QpJur2dWWDjF6C758FbBw5vm12BaVipnK" assert message.model == "claude-3-opus-latest" assert message.role == "assistant" assert message.stop_reason == "end_turn" assert message.stop_sequence is None assert message.type == "message" assert len(message.content) == 1 content = message.content[0] assert content.type == "text" assert content.text == "Hello there!" assert [e.type for e in events] == [ "message_start", "content_block_start", "content_block_delta", "text", "content_block_delta", "text", "content_block_delta", "text", "content_block_stop", "message_delta", ] def assert_tool_use_response(events: list[ParsedMessageStreamEvent[None]], message: Message) -> None: assert message.id == "msg_019Q1hrJbZG26Fb9BQhrkHEr" assert message.model == "claude-sonnet-4-20250514" assert message.role == "assistant" assert message.stop_reason == "tool_use" assert message.stop_sequence is None assert message.type == "message" assert len(message.content) == 2 content = message.content[0] assert content.type == "text" assert content.text == "I'll check the current weather in Paris for you." tool_use = message.content[1] assert tool_use.type == "tool_use" assert tool_use.id == "toolu_01NRLabsLyVHZPKxbKvkfSMn" assert tool_use.name == "get_weather" assert tool_use.input == { "location": "Paris", } assert message.usage.input_tokens == 377 assert message.usage.output_tokens == 65 assert message.usage.cache_creation_input_tokens == 0 assert message.usage.cache_read_input_tokens == 0 assert message.usage.service_tier == "standard" assert message.usage.server_tool_use == None assert [e.type for e in events] == [ "message_start", "content_block_start", "content_block_delta", "text", "content_block_delta", "text", "content_block_stop", "content_block_start", "content_block_delta", "input_json", "content_block_delta", "input_json", "content_block_delta", "input_json", "content_block_delta", "input_json", "content_block_delta", "input_json", "content_block_stop", "message_delta", ] def assert_refusal_response(message: Message) -> None: assert message.stop_reason == "refusal" assert message.stop_details is not None assert message.stop_details.type == "refusal" assert message.stop_details.category == "cyber" assert message.stop_details.explanation == "This request was refused due to policy." class TestSyncMessages: @pytest.mark.respx(base_url=base_url) def test_basic_response(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, content=get_response("basic_response.txt")) ) with sync_client.messages.stream( max_tokens=1024, messages=[ { "role": "user", "content": "Say hello there!", } ], model="claude-3-opus-latest", ) as stream: with pytest.warns(DeprecationWarning): assert isinstance(cast(Any, stream), Stream) assert_basic_response([event for event in stream], stream.get_final_message()) @pytest.mark.respx(base_url=base_url) def test_context_manager(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, content=get_response("basic_response.txt")) ) with sync_client.messages.stream( max_tokens=1024, messages=[ { "role": "user", "content": "Say hello there!", } ], model="claude-3-opus-latest", ) as stream: assert not stream.response.is_closed # response should be closed even if the body isn't read assert stream.response.is_closed @pytest.mark.respx(base_url=base_url) def test_deprecated_model_warning_stream(self, respx_mock: MockRouter) -> None: for deprecated_model in DEPRECATED_MODELS: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, content=get_response("basic_response.txt")) ) with pytest.warns(DeprecationWarning, match=f"The model '{deprecated_model}' is deprecated"): with sync_client.messages.stream( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model=deprecated_model, ) as stream: # Consume the stream to ensure the warning is triggered stream.until_done() @pytest.mark.respx(base_url=base_url) def test_tool_use(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, content=get_response("tool_use_response.txt")) ) with sync_client.messages.stream( max_tokens=1024, messages=[ { "role": "user", "content": "Say hello there!", } ], model="claude-sonnet-4-5", ) as stream: with pytest.warns(DeprecationWarning): assert isinstance(cast(Any, stream), Stream) assert_tool_use_response([event for event in stream], stream.get_final_message()) @pytest.mark.respx(base_url=base_url) def test_refusal_stop_details_propagated(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, content=get_response("refusal_response.txt")) ) with sync_client.messages.stream( max_tokens=1024, messages=[{"role": "user", "content": "Say hello there!"}], model="claude-opus-4-7", ) as stream: assert_refusal_response(stream.get_final_message()) class TestAsyncMessages: @pytest.mark.asyncio @pytest.mark.respx(base_url=base_url) async def test_basic_response(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, content=to_async_iter(get_response("basic_response.txt"))) ) async with async_client.messages.stream( max_tokens=1024, messages=[ { "role": "user", "content": "Say hello there!", } ], model="claude-3-opus-latest", ) as stream: with pytest.warns(DeprecationWarning): assert isinstance(cast(Any, stream), AsyncStream) assert_basic_response([event async for event in stream], await stream.get_final_message()) @pytest.mark.asyncio @pytest.mark.respx(base_url=base_url) async def test_context_manager(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, content=to_async_iter(get_response("basic_response.txt"))) ) async with async_client.messages.stream( max_tokens=1024, messages=[ { "role": "user", "content": "Say hello there!", } ], model="claude-3-opus-latest", ) as stream: assert not stream.response.is_closed # response should be closed even if the body isn't read assert stream.response.is_closed @pytest.mark.asyncio @pytest.mark.respx(base_url=base_url) async def test_deprecated_model_warning_stream(self, respx_mock: MockRouter) -> None: for deprecated_model in DEPRECATED_MODELS: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, content=to_async_iter(get_response("basic_response.txt"))) ) with pytest.warns(DeprecationWarning, match=f"The model '{deprecated_model}' is deprecated"): async with async_client.messages.stream( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model=deprecated_model, ) as stream: # Consume the stream to ensure the warning is triggered await stream.get_final_message() @pytest.mark.asyncio @pytest.mark.respx(base_url=base_url) async def test_tool_use(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, content=to_async_iter(get_response("tool_use_response.txt"))) ) async with async_client.messages.stream( max_tokens=1024, messages=[ { "role": "user", "content": "Say hello there!", } ], model="claude-sonnet-4-5", ) as stream: with pytest.warns(DeprecationWarning): assert isinstance(cast(Any, stream), AsyncStream) assert_tool_use_response([event async for event in stream], await stream.get_final_message()) @pytest.mark.asyncio @pytest.mark.respx(base_url=base_url) async def test_refusal_stop_details_propagated(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, content=to_async_iter(get_response("refusal_response.txt"))) ) async with async_client.messages.stream( max_tokens=1024, messages=[{"role": "user", "content": "Say hello there!"}], model="claude-opus-4-7", ) as stream: assert_refusal_response(await stream.get_final_message()) @pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) def test_stream_method_definition_in_sync(sync: bool) -> None: client: Anthropic | AsyncAnthropic = sync_client if sync else async_client assert_signatures_in_sync( client.messages.create, client.messages.stream, exclude_params={"stream"}, ) # go through all the ContentBlock types to make sure the type alias is up to date # with any type that has an input property of type object @pytest.mark.skipif(PYDANTIC_V1, reason="only applicable in pydantic v2") def test_tracks_tool_input_type_alias_is_up_to_date() -> None: from typing import get_args from pydantic import BaseModel from anthropic.types.content_block import ContentBlock # Get the content block union type content_block_union = get_args(ContentBlock)[0] # Get all types from ContentBlock union content_block_types = get_args(content_block_union) # Types that should have an input property types_with_input: Set[Any] = set() # Check each type to see if it has an input property in its model_fields for block_type in content_block_types: if issubclass(block_type, BaseModel) and "input" in block_type.model_fields: types_with_input.add(block_type) # Get the types included in TRACKS_TOOL_INPUT tracked_types = TRACKS_TOOL_INPUT # Make sure all types with input are tracked for block_type in types_with_input: assert block_type in tracked_types, ( f"ContentBlock type {block_type.__name__} has an input property, " f"but is not included in TRACKS_TOOL_INPUT. You probably need to update the TRACKS_TOOL_INPUT type alias." ) anthropic-sdk-python-0.120.2/tests/lib/streaming/test_parsed_content_blocks.py000066400000000000000000000071441523216435200276310ustar00rootroot00000000000000from __future__ import annotations from typing import Any, Set, cast from typing_extensions import get_args, get_origin import httpx import pytest from anthropic.types.beta import BetaFallbackBlock from anthropic.types.content_block import ContentBlock from anthropic.types.parsed_message import ParsedContentBlock from anthropic.types.beta.beta_usage import BetaUsage from anthropic.lib.streaming._beta_messages import accumulate_event from anthropic.types.beta.beta_content_block import BetaContentBlock from anthropic.types.beta.parsed_beta_message import ParsedBetaMessage, ParsedBetaContentBlock def _union_members(union: Any) -> Set[type]: """Return the runtime classes in an `Annotated[Union[...], ...]` type alias.""" annotated_args = get_args(union) assert annotated_args, f"expected an Annotated union, got {union!r}" members: Set[type] = set() for member in get_args(annotated_args[0]): # unwrap generic subscriptions, e.g. ParsedBetaTextBlock[ResponseFormatT] members.add(get_origin(member) or member) return members def _to_generated(member: type) -> type: """Map a Parsed* wrapper to the generated block class it stands in for.""" if member.__name__.startswith("Parsed"): return member.__bases__[0] return member @pytest.mark.parametrize( ("parsed_union", "generated_union", "parsed_file"), [ (ParsedBetaContentBlock, BetaContentBlock, "src/anthropic/types/beta/parsed_beta_message.py"), (ParsedContentBlock, ContentBlock, "src/anthropic/types/parsed_message.py"), ], ids=["beta", "non-beta"], ) def test_parsed_union_matches_generated_union(parsed_union: Any, generated_union: Any, parsed_file: str) -> None: """The hand-written Parsed*ContentBlock unions must track the generated unions. If a block type only exists in the generated union, streaming snapshots construct it through the parsed union's permissive fallback and it lands as the wrong class at runtime (and the wrong type statically). """ parsed_members = {_to_generated(member) for member in _union_members(parsed_union)} generated_members = _union_members(generated_union) missing = sorted(member.__name__ for member in generated_members - parsed_members) extra = sorted(member.__name__ for member in parsed_members - generated_members) assert not missing and not extra, ( f"the hand-written parsed content block union in {parsed_file} is out of sync " f"with the generated union: missing={missing} extra={extra}. " f"Update the union in {parsed_file} to match the generated content block union." ) def test_streamed_fallback_block_is_constructed_as_fallback_block() -> None: snapshot = ParsedBetaMessage( id="msg_123", type="message", role="assistant", content=[], model="claude-sonnet-4-5", stop_reason=None, stop_sequence=None, usage=BetaUsage(input_tokens=10, output_tokens=10), ) event = { "type": "content_block_start", "index": 0, "content_block": { "type": "fallback", "from": {"model": "claude-sonnet-4-5"}, "to": {"model": "claude-haiku-4-5"}, "trigger": {"type": "refusal", "category": None}, }, } message = accumulate_event( event=cast(Any, event), current_snapshot=snapshot, request_headers=httpx.Headers(), ) block = message.content[0] assert isinstance(block, BetaFallbackBlock) assert block.type == "fallback" assert block.from_.model == "claude-sonnet-4-5" assert block.to.model == "claude-haiku-4-5" anthropic-sdk-python-0.120.2/tests/lib/streaming/test_partial_json.py000066400000000000000000000142141523216435200257450ustar00rootroot00000000000000import copy from typing import List, cast import httpx from anthropic.types.beta import BetaDirectCaller, BetaToolUseBlock, BetaInputJSONDelta, BetaRawContentBlockDeltaEvent from anthropic.types.tool_use_block import ToolUseBlock from anthropic.types.beta.beta_usage import BetaUsage from anthropic.lib.streaming._beta_messages import accumulate_event from anthropic.types.beta.parsed_beta_message import ParsedBetaMessage class TestPartialJson: def test_trailing_strings_mode_header(self) -> None: """Test behavior differences with and without the beta header for JSON parsing.""" message = ParsedBetaMessage( id="msg_123", type="message", role="assistant", content=[ BetaToolUseBlock( type="tool_use", input={}, id="tool_123", name="test_tool", caller=BetaDirectCaller(type="direct"), ) ], model="claude-sonnet-4-5", stop_reason=None, stop_sequence=None, usage=BetaUsage(input_tokens=10, output_tokens=10), ) # Test case 1: Complete JSON complete_json = '{"key": "value"}' event_complete = BetaRawContentBlockDeltaEvent( type="content_block_delta", index=0, delta=BetaInputJSONDelta(type="input_json_delta", partial_json=complete_json), ) # Both modes should handle complete JSON the same way message1 = accumulate_event( event=event_complete, current_snapshot=copy.deepcopy(message), request_headers=httpx.Headers({"some-header": "value"}), ) message2 = accumulate_event( event=event_complete, current_snapshot=copy.deepcopy(message), request_headers=httpx.Headers({"anthropic-beta": "fine-grained-tool-streaming-2025-05-14"}), ) # Both should parse complete JSON correctly assert cast(ToolUseBlock, message1.content[0]).input == {"key": "value"} assert cast(ToolUseBlock, message2.content[0]).input == {"key": "value"} # Test case 2: Incomplete JSON with trailing string that will be treated differently # Here we want to create a situation where regular mode and trailing strings mode behave differently incomplete_json = '{"items": ["item1", "item2"], "unfinished_field": "incomplete value' event_incomplete = BetaRawContentBlockDeltaEvent( type="content_block_delta", index=0, delta=BetaInputJSONDelta(type="input_json_delta", partial_json=incomplete_json), ) # Without beta header (standard mode) message_standard = accumulate_event( event=event_incomplete, current_snapshot=copy.deepcopy(message), request_headers=httpx.Headers({"some-header": "value"}), ) # With beta header (trailing strings mode) message_trailing = accumulate_event( event=event_incomplete, current_snapshot=copy.deepcopy(message), request_headers=httpx.Headers({"anthropic-beta": "fine-grained-tool-streaming-2025-05-14"}), ) # Get the tool use blocks standard_tool = cast(ToolUseBlock, message_standard.content[0]) trailing_tool = cast(ToolUseBlock, message_trailing.content[0]) # Both should have the valid complete part of the JSON assert isinstance(standard_tool.input, dict) assert isinstance(trailing_tool.input, dict) standard_input = standard_tool.input # type: ignore trailing_input = trailing_tool.input # type: ignore # The input should have the items array in both cases items_standard = cast(List[str], standard_input["items"]) items_trailing = cast(List[str], trailing_input["items"]) assert items_standard == ["item1", "item2"] assert items_trailing == ["item1", "item2"] # The key difference is how they handle the incomplete field: # Standard mode should not include the incomplete field assert "unfinished_field" not in standard_input # Trailing strings mode should include the incomplete field assert "unfinished_field" in trailing_input assert trailing_input["unfinished_field"] == "incomplete value" # test that with invalid JSON we throw the correct error def test_partial_json_with_invalid_json(self) -> None: """Test that invalid JSON raises an error.""" message = ParsedBetaMessage( id="msg_123", type="message", role="assistant", content=[ BetaToolUseBlock( type="tool_use", input={}, id="tool_123", name="test_tool", caller=BetaDirectCaller(type="direct"), ) ], model="claude-sonnet-4-5", stop_reason=None, stop_sequence=None, usage=BetaUsage(input_tokens=10, output_tokens=10), ) # Invalid JSON input invalid_json = '{"key": "value", "incomplete_field": bad_value' event_invalid = BetaRawContentBlockDeltaEvent( type="content_block_delta", index=0, delta=BetaInputJSONDelta(type="input_json_delta", partial_json=invalid_json), ) # Expect an error when trying to accumulate the invalid JSON try: accumulate_event( event=event_invalid, current_snapshot=copy.deepcopy(message), request_headers=httpx.Headers({"anthropic-beta": "fine-grained-tool-streaming-2025-05-14"}), ) raise AssertionError("Expected ValueError for invalid JSON, but no error was raised.") except ValueError as e: assert str(e).startswith( "Unable to parse tool parameter JSON from model. Please retry your request or adjust your prompt." ) except Exception as e: raise AssertionError(f"Unexpected error type: {type(e).__name__} with message: {str(e)}") from e anthropic-sdk-python-0.120.2/tests/lib/test_aws.py000066400000000000000000000407331523216435200220660ustar00rootroot00000000000000import re from typing import cast from typing_extensions import Protocol import httpx import pytest from respx import MockRouter from anthropic import AnthropicAWS, AsyncAnthropicAWS from anthropic._exceptions import AnthropicError from anthropic.lib.credentials import StaticToken class MockRequestCall(Protocol): request: httpx.Request # --- Initialization --- def test_init_api_key_mode() -> None: client = AnthropicAWS(api_key="test-key", aws_region="us-east-1", workspace_id="ws-123") assert client.api_key == "test-key" assert client._use_sigv4 is False def test_init_sigv4_explicit_creds() -> None: client = AnthropicAWS( aws_access_key="AKID", aws_secret_key="secret", aws_region="us-west-2", workspace_id="ws-123", ) assert client._use_sigv4 is True assert client.api_key is None assert client.aws_access_key == "AKID" assert client.aws_secret_key == "secret" def test_init_sigv4_profile() -> None: client = AnthropicAWS(aws_profile="my-profile", aws_region="eu-west-1", workspace_id="ws-123") assert client._use_sigv4 is True assert client.aws_profile == "my-profile" def test_init_sigv4_default_credential_chain() -> None: client = AnthropicAWS(aws_region="us-east-1", workspace_id="ws-123") assert client._use_sigv4 is True assert client.api_key is None def test_init_requires_region_for_sigv4(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.delenv("AWS_REGION", raising=False) with pytest.raises(AnthropicError, match="No AWS region was provided"): AnthropicAWS(aws_access_key="AKID", aws_secret_key="secret", workspace_id="ws-123") def test_init_requires_workspace_id(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.delenv("ANTHROPIC_AWS_WORKSPACE_ID", raising=False) with pytest.raises(AnthropicError, match="No workspace ID found"): AnthropicAWS(api_key="test-key", aws_region="us-east-1") def test_init_workspace_id_from_env(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_AWS_WORKSPACE_ID", "env-workspace") client = AnthropicAWS(api_key="test-key", aws_region="us-east-1") assert client.workspace_id == "env-workspace" def test_init_async_api_key_mode() -> None: client = AsyncAnthropicAWS(api_key="test-key", aws_region="us-east-1", workspace_id="ws-123") assert client.api_key == "test-key" assert client._use_sigv4 is False def test_init_async_sigv4() -> None: client = AsyncAnthropicAWS( aws_access_key="AKID", aws_secret_key="secret", aws_region="us-west-2", workspace_id="ws-123", ) assert client._use_sigv4 is True def test_init_async_requires_workspace_id(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.delenv("ANTHROPIC_AWS_WORKSPACE_ID", raising=False) with pytest.raises(AnthropicError, match="No workspace ID found"): AsyncAnthropicAWS(api_key="test-key", aws_region="us-east-1") # --- Partial credential validation --- def test_partial_creds_access_key_only() -> None: with pytest.raises(ValueError, match="aws_access_key.*without.*aws_secret_key"): AnthropicAWS(aws_access_key="AKID", aws_region="us-east-1", workspace_id="ws-123") def test_partial_creds_secret_key_only() -> None: with pytest.raises(ValueError, match="aws_secret_key.*without.*aws_access_key"): AnthropicAWS(aws_secret_key="secret", aws_region="us-east-1", workspace_id="ws-123") def test_partial_creds_async_access_key_only() -> None: with pytest.raises(ValueError, match="aws_access_key.*without.*aws_secret_key"): AsyncAnthropicAWS(aws_access_key="AKID", aws_region="us-east-1", workspace_id="ws-123") # --- skipAuth --- def test_skip_auth_no_workspace_required(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.delenv("ANTHROPIC_AWS_WORKSPACE_ID", raising=False) client = AnthropicAWS(skip_auth=True, base_url="https://custom.example.com") assert client._skip_auth is True assert client._use_sigv4 is False assert client.workspace_id is None def test_skip_auth_no_region_or_base_url_required(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.delenv("AWS_REGION", raising=False) monkeypatch.delenv("ANTHROPIC_AWS_BASE_URL", raising=False) monkeypatch.delenv("ANTHROPIC_AWS_WORKSPACE_ID", raising=False) client = AnthropicAWS(skip_auth=True) assert client._skip_auth is True def test_skip_auth_async(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.delenv("ANTHROPIC_AWS_WORKSPACE_ID", raising=False) client = AsyncAnthropicAWS(skip_auth=True, base_url="https://custom.example.com") assert client._skip_auth is True assert client._use_sigv4 is False @pytest.mark.filterwarnings("ignore::DeprecationWarning") @pytest.mark.respx() def test_skip_auth_no_auth_headers(respx_mock: MockRouter) -> None: respx_mock.post(re.compile(r"https://custom\.example\.com/.*")).mock( return_value=httpx.Response(200, json={"foo": "bar"}) ) client = AnthropicAWS(skip_auth=True, base_url="https://custom.example.com") client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-sonnet-4-20250514", ) calls = cast("list[MockRequestCall]", respx_mock.calls) assert len(calls) == 1 assert "X-Api-Key" not in calls[0].request.headers assert "Authorization" not in calls[0].request.headers assert "X-Amz-Date" not in calls[0].request.headers assert "anthropic-workspace-id" not in calls[0].request.headers # --- Environment Variables --- def test_env_api_key(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_AWS_API_KEY", "env-key") monkeypatch.delenv("AWS_REGION", raising=False) client = AnthropicAWS(base_url="https://example.com", workspace_id="ws-123") assert client.api_key == "env-key" assert client._use_sigv4 is False def test_env_region(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("AWS_REGION", "ap-southeast-1") client = AnthropicAWS(api_key="test-key", workspace_id="ws-123") assert client.aws_region == "ap-southeast-1" def test_env_base_url(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_AWS_BASE_URL", "https://custom-gateway.example.com") client = AnthropicAWS(api_key="test-key", workspace_id="ws-123") assert str(client.base_url).rstrip("/") == "https://custom-gateway.example.com" # --- Auth Precedence --- def test_api_key_arg_takes_precedence_over_env(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_AWS_API_KEY", "env-key") client = AnthropicAWS(api_key="arg-key", aws_region="us-east-1", workspace_id="ws-123") assert client.api_key == "arg-key" assert client._use_sigv4 is False def test_explicit_aws_creds_suppress_env_api_key(monkeypatch: pytest.MonkeyPatch) -> None: """Explicit SigV4 constructor args should suppress ANTHROPIC_AWS_API_KEY env var.""" monkeypatch.setenv("ANTHROPIC_AWS_API_KEY", "env-key") client = AnthropicAWS( aws_access_key="AKID", aws_secret_key="secret", aws_region="us-east-1", workspace_id="ws-123", ) assert client._use_sigv4 is True assert client.api_key is None def test_aws_profile_suppresses_env_api_key(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_AWS_API_KEY", "env-key") client = AnthropicAWS(aws_profile="my-profile", aws_region="us-east-1", workspace_id="ws-123") assert client._use_sigv4 is True assert client.api_key is None # --- Region / Base URL --- def test_region_from_constructor() -> None: client = AnthropicAWS(api_key="test-key", aws_region="eu-central-1", workspace_id="ws-123") assert client.aws_region == "eu-central-1" assert str(client.base_url).rstrip("/") == "https://aws-external-anthropic.eu-central-1.api.aws" def test_region_constructor_overrides_env(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("AWS_REGION", "us-west-2") client = AnthropicAWS(api_key="test-key", aws_region="eu-west-1", workspace_id="ws-123") assert client.aws_region == "eu-west-1" assert str(client.base_url).rstrip("/") == "https://aws-external-anthropic.eu-west-1.api.aws" def test_base_url_override() -> None: client = AnthropicAWS( api_key="test-key", aws_region="us-east-1", base_url="https://custom.example.com", workspace_id="ws-123", ) assert str(client.base_url).rstrip("/") == "https://custom.example.com" def test_api_key_mode_no_region_with_base_url() -> None: """API key mode should work without a region if base_url is provided.""" client = AnthropicAWS(api_key="test-key", base_url="https://custom.example.com", workspace_id="ws-123") assert client.aws_region is None assert client._use_sigv4 is False def test_api_key_mode_no_region_no_base_url_errors(monkeypatch: pytest.MonkeyPatch) -> None: """API key mode without region or base_url should error.""" monkeypatch.delenv("AWS_REGION", raising=False) monkeypatch.delenv("ANTHROPIC_AWS_BASE_URL", raising=False) with pytest.raises(AnthropicError, match="No AWS region was provided and no base_url"): AnthropicAWS(api_key="test-key", workspace_id="ws-123") # --- Resources --- def test_has_all_resources() -> None: client = AnthropicAWS(api_key="test-key", aws_region="us-east-1", workspace_id="ws-123") assert client.messages is not None assert client.beta is not None assert client.models is not None assert client.completions is not None def test_async_has_all_resources() -> None: client = AsyncAnthropicAWS(api_key="test-key", aws_region="us-east-1", workspace_id="ws-123") assert client.messages is not None assert client.beta is not None assert client.models is not None assert client.completions is not None # --- Request behavior (API key mode) --- @pytest.mark.filterwarnings("ignore::DeprecationWarning") @pytest.mark.respx() def test_api_key_request(respx_mock: MockRouter) -> None: respx_mock.post(re.compile(r"https://aws-external-anthropic\.us-east-1\.api\.aws/.*")).mock( return_value=httpx.Response(200, json={"foo": "bar"}) ) client = AnthropicAWS(api_key="test-key", aws_region="us-east-1", workspace_id="ws-123") client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-sonnet-4-20250514", ) calls = cast("list[MockRequestCall]", respx_mock.calls) assert len(calls) == 1 assert str(calls[0].request.url) == "https://aws-external-anthropic.us-east-1.api.aws/v1/messages" assert calls[0].request.headers["X-Api-Key"] == "test-key" @pytest.mark.filterwarnings("ignore::DeprecationWarning") @pytest.mark.respx() @pytest.mark.asyncio() async def test_api_key_request_async(respx_mock: MockRouter) -> None: respx_mock.post(re.compile(r"https://aws-external-anthropic\.us-east-1\.api\.aws/.*")).mock( return_value=httpx.Response(200, json={"foo": "bar"}) ) client = AsyncAnthropicAWS(api_key="test-key", aws_region="us-east-1", workspace_id="ws-123") await client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-sonnet-4-20250514", ) calls = cast("list[MockRequestCall]", respx_mock.calls) assert len(calls) == 1 assert str(calls[0].request.url) == "https://aws-external-anthropic.us-east-1.api.aws/v1/messages" assert calls[0].request.headers["X-Api-Key"] == "test-key" @pytest.mark.filterwarnings("ignore::DeprecationWarning") @pytest.mark.respx() def test_retries(respx_mock: MockRouter) -> None: respx_mock.post(re.compile(r"https://aws-external-anthropic\.us-east-1\.api\.aws/.*")).mock( side_effect=[ httpx.Response(500, json={"error": "server error"}, headers={"retry-after-ms": "10"}), httpx.Response(200, json={"foo": "bar"}), ] ) client = AnthropicAWS(api_key="test-key", aws_region="us-east-1", workspace_id="ws-123") client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-sonnet-4-20250514", ) calls = cast("list[MockRequestCall]", respx_mock.calls) assert len(calls) == 2 # --- copy / with_options --- def test_copy_preserves_aws_options() -> None: client = AnthropicAWS( aws_access_key="AKID", aws_secret_key="secret", aws_region="us-east-1", aws_profile="my-profile", aws_session_token="token", workspace_id="ws-123", ) copied = client.copy() assert copied.aws_access_key == "AKID" assert copied.aws_secret_key == "secret" assert copied.aws_region == "us-east-1" assert copied.aws_profile == "my-profile" assert copied.aws_session_token == "token" assert copied._use_sigv4 is True def test_copy_overrides_aws_options() -> None: client = AnthropicAWS( aws_access_key="AKID", aws_secret_key="secret", aws_region="us-east-1", workspace_id="ws-123", ) copied = client.copy(aws_region="eu-west-1") assert copied.aws_region == "eu-west-1" # base_url is not re-derived from region on copy — must be overridden explicitly assert str(copied.base_url).rstrip("/") == "https://aws-external-anthropic.us-east-1.api.aws" copied2 = client.copy( aws_region="eu-west-1", base_url="https://aws-external-anthropic.eu-west-1.api.aws", ) assert str(copied2.base_url).rstrip("/") == "https://aws-external-anthropic.eu-west-1.api.aws" def test_copy_accepts_credentials_none_noop() -> None: # `credentials=None` is accepted as a no-op: the AWS client authenticates with # SigV4, not a token provider, so there is nothing to clear. The copy stays SigV4. client = AnthropicAWS( aws_access_key="AKID", aws_secret_key="secret", aws_region="us-east-1", workspace_id="ws-123", ) copied = client.copy(credentials=None) assert copied._use_sigv4 is True assert copied.workspace_id == "ws-123" assert copied.aws_region == "us-east-1" def test_copy_accepts_credentials_none_noop_async() -> None: client = AsyncAnthropicAWS( aws_access_key="AKID", aws_secret_key="secret", aws_region="us-east-1", workspace_id="ws-123", ) copied = client.copy(credentials=None) assert copied._use_sigv4 is True assert copied.workspace_id == "ws-123" assert copied.aws_region == "us-east-1" def test_copy_rejects_real_credentials_provider() -> None: client = AnthropicAWS( aws_access_key="AKID", aws_secret_key="secret", aws_region="us-east-1", workspace_id="ws-123", ) with pytest.raises(TypeError, match="does not support a `credentials` provider"): client.copy(credentials=StaticToken("token")) def test_copy_rejects_real_credentials_provider_async() -> None: client = AsyncAnthropicAWS( aws_access_key="AKID", aws_secret_key="secret", aws_region="us-east-1", workspace_id="ws-123", ) with pytest.raises(TypeError, match="does not support a `credentials` provider"): client.copy(credentials=StaticToken("token")) def test_scoped_bearer_client_helper_on_aws() -> None: # Regression: the environment poller / worker / session-tool-runner build a # scoped sub-client via `_copy_client_with_bearer_auth`, which calls # `client.copy(credentials=None, ...)`. That must work on the AWS client and # yield a client that still signs with SigV4 (the bearer token is unused). from anthropic.lib._scoped_client import _copy_client_with_bearer_auth client = AnthropicAWS( aws_access_key="AKID", aws_secret_key="secret", aws_region="us-east-1", workspace_id="ws-123", ) scoped = _copy_client_with_bearer_auth(client, auth_token="unused-under-sigv4", helper="environments-work-poller") assert isinstance(scoped, AnthropicAWS) assert scoped._use_sigv4 is True assert scoped.workspace_id == "ws-123" def test_scoped_bearer_client_helper_on_aws_async() -> None: from anthropic.lib._scoped_client import _copy_client_with_bearer_auth client = AsyncAnthropicAWS( aws_access_key="AKID", aws_secret_key="secret", aws_region="us-east-1", workspace_id="ws-123", ) scoped = _copy_client_with_bearer_auth(client, auth_token="unused-under-sigv4", helper="environments-worker") assert isinstance(scoped, AsyncAnthropicAWS) assert scoped._use_sigv4 is True assert scoped.workspace_id == "ws-123" anthropic-sdk-python-0.120.2/tests/lib/test_aws_auth.py000066400000000000000000000565331523216435200231140ustar00rootroot00000000000000from __future__ import annotations import re from unittest.mock import patch import httpx import pytest from anthropic.lib.aws._auth import get_auth_headers from anthropic.lib.aws._credentials import ( resolve_region, resolve_api_key, resolve_base_url, resolve_auth_mode, resolve_workspace_id, validate_credentials, ) # --- validate_credentials --- class TestValidateCredentials: def test_both_provided_passes(self) -> None: validate_credentials(aws_access_key="AKID", aws_secret_key="secret") def test_neither_provided_passes(self) -> None: validate_credentials(aws_access_key=None, aws_secret_key=None) def test_access_key_only_raises(self) -> None: with pytest.raises(ValueError, match="aws_access_key.*without.*aws_secret_key"): validate_credentials(aws_access_key="AKID", aws_secret_key=None) def test_secret_key_only_raises(self) -> None: with pytest.raises(ValueError, match="aws_secret_key.*without.*aws_access_key"): validate_credentials(aws_access_key=None, aws_secret_key="secret") # --- resolve_auth_mode --- class TestResolveAuthMode: def test_api_key_arg_returns_false(self) -> None: assert ( resolve_auth_mode( api_key="key", aws_access_key=None, aws_secret_key=None, aws_profile=None, ) is False ) def test_explicit_creds_returns_true(self) -> None: assert ( resolve_auth_mode( api_key=None, aws_access_key="AKID", aws_secret_key="secret", aws_profile=None, ) is True ) def test_access_key_alone_returns_true(self) -> None: assert ( resolve_auth_mode( api_key=None, aws_access_key="AKID", aws_secret_key=None, aws_profile=None, ) is True ) def test_profile_returns_true(self) -> None: assert ( resolve_auth_mode( api_key=None, aws_access_key=None, aws_secret_key=None, aws_profile="my-profile", ) is True ) def test_env_api_key_returns_false(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_AWS_API_KEY", "env-key") assert ( resolve_auth_mode( api_key=None, aws_access_key=None, aws_secret_key=None, aws_profile=None, ) is False ) def test_no_args_no_env_defaults_to_sigv4(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.delenv("ANTHROPIC_AWS_API_KEY", raising=False) assert ( resolve_auth_mode( api_key=None, aws_access_key=None, aws_secret_key=None, aws_profile=None, ) is True ) def test_explicit_creds_suppress_env_api_key(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_AWS_API_KEY", "env-key") assert ( resolve_auth_mode( api_key=None, aws_access_key="AKID", aws_secret_key="secret", aws_profile=None, ) is True ) def test_profile_suppresses_env_api_key(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_AWS_API_KEY", "env-key") assert ( resolve_auth_mode( api_key=None, aws_access_key=None, aws_secret_key=None, aws_profile="my-profile", ) is True ) def test_api_key_arg_beats_explicit_creds(self) -> None: """api_key constructor arg takes highest precedence.""" assert ( resolve_auth_mode( api_key="key", aws_access_key="AKID", aws_secret_key="secret", aws_profile=None, ) is False ) def test_custom_env_var(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("AWS_BEARER_TOKEN_BEDROCK", "mantle-key") monkeypatch.delenv("ANTHROPIC_AWS_API_KEY", raising=False) assert ( resolve_auth_mode( api_key=None, aws_access_key=None, aws_secret_key=None, aws_profile=None, api_key_env_vars=("AWS_BEARER_TOKEN_BEDROCK",), ) is False ) def test_custom_env_var_not_set_defaults_to_sigv4(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.delenv("AWS_BEARER_TOKEN_BEDROCK", raising=False) monkeypatch.delenv("ANTHROPIC_AWS_API_KEY", raising=False) assert ( resolve_auth_mode( api_key=None, aws_access_key=None, aws_secret_key=None, aws_profile=None, api_key_env_vars=("AWS_BEARER_TOKEN_BEDROCK",), ) is True ) def test_env_var_fallback_chain_first_wins(self, monkeypatch: pytest.MonkeyPatch) -> None: """First env var in the chain takes priority.""" monkeypatch.setenv("AWS_BEARER_TOKEN_BEDROCK", "mantle-key") monkeypatch.setenv("ANTHROPIC_AWS_API_KEY", "aws-key") assert ( resolve_auth_mode( api_key=None, aws_access_key=None, aws_secret_key=None, aws_profile=None, api_key_env_vars=("AWS_BEARER_TOKEN_BEDROCK", "ANTHROPIC_AWS_API_KEY"), ) is False ) def test_env_var_fallback_chain_falls_through(self, monkeypatch: pytest.MonkeyPatch) -> None: """Falls back to second env var when first is not set.""" monkeypatch.delenv("AWS_BEARER_TOKEN_BEDROCK", raising=False) monkeypatch.setenv("ANTHROPIC_AWS_API_KEY", "aws-key") assert ( resolve_auth_mode( api_key=None, aws_access_key=None, aws_secret_key=None, aws_profile=None, api_key_env_vars=("AWS_BEARER_TOKEN_BEDROCK", "ANTHROPIC_AWS_API_KEY"), ) is False ) def test_env_var_fallback_chain_none_set(self, monkeypatch: pytest.MonkeyPatch) -> None: """Defaults to SigV4 when no env vars in the chain are set.""" monkeypatch.delenv("AWS_BEARER_TOKEN_BEDROCK", raising=False) monkeypatch.delenv("ANTHROPIC_AWS_API_KEY", raising=False) assert ( resolve_auth_mode( api_key=None, aws_access_key=None, aws_secret_key=None, aws_profile=None, api_key_env_vars=("AWS_BEARER_TOKEN_BEDROCK", "ANTHROPIC_AWS_API_KEY"), ) is True ) # --- resolve_api_key --- class TestResolveApiKey: def test_returns_arg_when_provided(self) -> None: assert resolve_api_key(api_key="arg-key", use_sigv4=False) == "arg-key" def test_returns_none_for_sigv4(self) -> None: assert resolve_api_key(api_key=None, use_sigv4=True) is None def test_returns_env_when_not_sigv4(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_AWS_API_KEY", "env-key") assert resolve_api_key(api_key=None, use_sigv4=False) == "env-key" def test_returns_none_when_not_sigv4_and_no_env(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.delenv("ANTHROPIC_AWS_API_KEY", raising=False) assert resolve_api_key(api_key=None, use_sigv4=False) is None def test_arg_takes_precedence_over_env(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_AWS_API_KEY", "env-key") assert resolve_api_key(api_key="arg-key", use_sigv4=False) == "arg-key" def test_sigv4_ignores_env(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_AWS_API_KEY", "env-key") assert resolve_api_key(api_key=None, use_sigv4=True) is None def test_custom_env_var(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("AWS_BEARER_TOKEN_BEDROCK", "mantle-key") assert ( resolve_api_key( api_key=None, use_sigv4=False, api_key_env_vars=("AWS_BEARER_TOKEN_BEDROCK",), ) == "mantle-key" ) def test_env_var_fallback_chain_first_wins(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("AWS_BEARER_TOKEN_BEDROCK", "mantle-key") monkeypatch.setenv("ANTHROPIC_AWS_API_KEY", "aws-key") assert ( resolve_api_key( api_key=None, use_sigv4=False, api_key_env_vars=("AWS_BEARER_TOKEN_BEDROCK", "ANTHROPIC_AWS_API_KEY"), ) == "mantle-key" ) def test_env_var_fallback_chain_falls_through(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.delenv("AWS_BEARER_TOKEN_BEDROCK", raising=False) monkeypatch.setenv("ANTHROPIC_AWS_API_KEY", "aws-key") assert ( resolve_api_key( api_key=None, use_sigv4=False, api_key_env_vars=("AWS_BEARER_TOKEN_BEDROCK", "ANTHROPIC_AWS_API_KEY"), ) == "aws-key" ) # --- resolve_region --- class TestResolveRegion: def test_returns_arg_when_provided(self) -> None: assert resolve_region("us-west-2") == "us-west-2" def test_returns_aws_region_env(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("AWS_REGION", "eu-west-1") assert resolve_region(None) == "eu-west-1" def test_returns_aws_default_region_env(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.delenv("AWS_REGION", raising=False) monkeypatch.setenv("AWS_DEFAULT_REGION", "ap-southeast-1") assert resolve_region(None) == "ap-southeast-1" def test_aws_region_takes_precedence_over_default(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("AWS_REGION", "us-east-1") monkeypatch.setenv("AWS_DEFAULT_REGION", "us-west-2") assert resolve_region(None) == "us-east-1" def test_arg_takes_precedence_over_env(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("AWS_REGION", "us-east-1") assert resolve_region("eu-central-1") == "eu-central-1" def test_returns_none_when_no_source(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.delenv("AWS_REGION", raising=False) monkeypatch.delenv("AWS_DEFAULT_REGION", raising=False) assert resolve_region(None) is None # --- resolve_workspace_id --- class TestResolveWorkspaceId: def test_returns_arg_when_provided(self) -> None: assert resolve_workspace_id("ws-123") == "ws-123" def test_returns_env_when_no_arg(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_AWS_WORKSPACE_ID", "env-ws") assert resolve_workspace_id(None) == "env-ws" def test_returns_none_when_no_source(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.delenv("ANTHROPIC_AWS_WORKSPACE_ID", raising=False) assert resolve_workspace_id(None) is None def test_arg_takes_precedence_over_env(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_AWS_WORKSPACE_ID", "env-ws") assert resolve_workspace_id("arg-ws") == "arg-ws" def test_custom_env_var(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_BEDROCK_MANTLE_WORKSPACE_ID", "mantle-ws") assert ( resolve_workspace_id( None, workspace_id_env_vars=("ANTHROPIC_BEDROCK_MANTLE_WORKSPACE_ID",), ) == "mantle-ws" ) def test_env_var_fallback_chain_first_wins(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_BEDROCK_MANTLE_WORKSPACE_ID", "mantle-ws") monkeypatch.setenv("ANTHROPIC_AWS_WORKSPACE_ID", "aws-ws") assert ( resolve_workspace_id( None, workspace_id_env_vars=("ANTHROPIC_BEDROCK_MANTLE_WORKSPACE_ID", "ANTHROPIC_AWS_WORKSPACE_ID"), ) == "mantle-ws" ) def test_env_var_fallback_chain_falls_through(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.delenv("ANTHROPIC_BEDROCK_MANTLE_WORKSPACE_ID", raising=False) monkeypatch.setenv("ANTHROPIC_AWS_WORKSPACE_ID", "aws-ws") assert ( resolve_workspace_id( None, workspace_id_env_vars=("ANTHROPIC_BEDROCK_MANTLE_WORKSPACE_ID", "ANTHROPIC_AWS_WORKSPACE_ID"), ) == "aws-ws" ) def test_env_var_fallback_chain_none_set(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.delenv("ANTHROPIC_BEDROCK_MANTLE_WORKSPACE_ID", raising=False) monkeypatch.delenv("ANTHROPIC_AWS_WORKSPACE_ID", raising=False) assert ( resolve_workspace_id( None, workspace_id_env_vars=("ANTHROPIC_BEDROCK_MANTLE_WORKSPACE_ID", "ANTHROPIC_AWS_WORKSPACE_ID"), ) is None ) # --- get_auth_headers --- class TestResolveBaseUrl: def test_returns_arg_when_provided(self) -> None: assert resolve_base_url("https://custom.example.com", region="us-east-1") == "https://custom.example.com" def test_returns_env_when_no_arg(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_AWS_BASE_URL", "https://env-gateway.example.com") assert resolve_base_url(None, region="us-east-1") == "https://env-gateway.example.com" def test_derives_from_region(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.delenv("ANTHROPIC_AWS_BASE_URL", raising=False) assert resolve_base_url(None, region="us-west-2") == "https://aws-external-anthropic.us-west-2.api.aws" def test_returns_none_when_no_source(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.delenv("ANTHROPIC_AWS_BASE_URL", raising=False) assert resolve_base_url(None, region=None) is None def test_arg_takes_precedence_over_env(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_AWS_BASE_URL", "https://env-gateway.example.com") assert resolve_base_url("https://arg.example.com", region="us-east-1") == "https://arg.example.com" def test_env_takes_precedence_over_region(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_AWS_BASE_URL", "https://env-gateway.example.com") assert resolve_base_url(None, region="us-east-1") == "https://env-gateway.example.com" def test_custom_url_template(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.delenv("ANTHROPIC_AWS_BASE_URL", raising=False) assert ( resolve_base_url( None, region="us-east-1", url_template="https://bedrock-mantle.{region}.api.aws/anthropic", ) == "https://bedrock-mantle.us-east-1.api.aws/anthropic" ) def test_custom_env_var(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_BEDROCK_MANTLE_BASE_URL", "https://mantle.example.com") assert ( resolve_base_url( None, region="us-east-1", base_url_env_vars=("ANTHROPIC_BEDROCK_MANTLE_BASE_URL",), ) == "https://mantle.example.com" ) def test_env_var_fallback_chain(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.delenv("ANTHROPIC_BEDROCK_MANTLE_BASE_URL", raising=False) monkeypatch.setenv("ANTHROPIC_AWS_BASE_URL", "https://aws-fallback.example.com") assert ( resolve_base_url( None, region="us-east-1", base_url_env_vars=("ANTHROPIC_BEDROCK_MANTLE_BASE_URL", "ANTHROPIC_AWS_BASE_URL"), ) == "https://aws-fallback.example.com" ) class TestGetAuthHeaders: def test_uses_service_name_parameter(self) -> None: """service_name is passed through to SigV4Auth, not hardcoded.""" headers = get_auth_headers( method="POST", url="https://gateway.us-east-1.api.aws/v1/messages", headers=httpx.Headers({"content-type": "application/json"}), aws_access_key="AKIAIOSFODNN7EXAMPLE", aws_secret_key="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", aws_session_token=None, region="us-east-1", profile=None, data='{"hello": "world"}', service_name="aws-external-anthropic", ) assert "Authorization" in headers assert "aws-external-anthropic" in headers["Authorization"] def test_different_service_name(self) -> None: """Mantle uses a different service name for SigV4 signing.""" headers = get_auth_headers( method="POST", url="https://bedrock-mantle.us-east-1.api.aws/anthropic/v1/messages", headers=httpx.Headers({"content-type": "application/json"}), aws_access_key="AKIAIOSFODNN7EXAMPLE", aws_secret_key="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", aws_session_token=None, region="us-east-1", profile=None, data='{"hello": "world"}', service_name="bedrock-mantle", ) assert "Authorization" in headers assert "bedrock-mantle" in headers["Authorization"] assert "aws-external-anthropic" not in headers["Authorization"] def test_returns_authorization_and_date_headers(self) -> None: headers = get_auth_headers( method="POST", url="https://gateway.us-east-1.api.aws/v1/messages", headers=httpx.Headers({"content-type": "application/json"}), aws_access_key="AKIAIOSFODNN7EXAMPLE", aws_secret_key="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", aws_session_token=None, region="us-east-1", profile=None, data='{"hello": "world"}', service_name="aws-external-anthropic", ) assert "Authorization" in headers assert headers["Authorization"].startswith("AWS4-HMAC-SHA256") assert "X-Amz-Date" in headers def test_includes_security_token_header(self) -> None: headers = get_auth_headers( method="POST", url="https://gateway.us-east-1.api.aws/v1/messages", headers=httpx.Headers({"content-type": "application/json"}), aws_access_key="AKIAIOSFODNN7EXAMPLE", aws_secret_key="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", aws_session_token="FwoGZXIvYXdzEBYaDH7example", region="us-east-1", profile=None, data='{"hello": "world"}', service_name="aws-external-anthropic", ) assert headers.get("X-Amz-Security-Token") == "FwoGZXIvYXdzEBYaDH7example" def test_strips_connection_header(self) -> None: """Connection header must not be signed (may be stripped by proxies).""" headers = get_auth_headers( method="POST", url="https://gateway.us-east-1.api.aws/v1/messages", headers=httpx.Headers({"content-type": "application/json", "connection": "keep-alive"}), aws_access_key="AKIAIOSFODNN7EXAMPLE", aws_secret_key="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", aws_session_token=None, region="us-east-1", profile=None, data='{"hello": "world"}', service_name="aws-external-anthropic", ) # The signed headers in Authorization should not include "connection" auth = headers["Authorization"] signed_headers_match = re.search(r"SignedHeaders=([^,]+)", auth) assert signed_headers_match is not None signed_headers = signed_headers_match.group(1).split(";") assert "connection" not in signed_headers def test_handles_null_body(self) -> None: headers = get_auth_headers( method="GET", url="https://gateway.us-east-1.api.aws/v1/models", headers=httpx.Headers({}), aws_access_key="AKIAIOSFODNN7EXAMPLE", aws_secret_key="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", aws_session_token=None, region="us-east-1", profile=None, data=None, service_name="aws-external-anthropic", ) assert "Authorization" in headers def test_includes_query_params_in_signing(self) -> None: """URL query params must be part of the signed request.""" headers_with_query = get_auth_headers( method="GET", url="https://gateway.us-east-1.api.aws/v1/models?limit=10", headers=httpx.Headers({}), aws_access_key="AKIAIOSFODNN7EXAMPLE", aws_secret_key="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", aws_session_token=None, region="us-east-1", profile=None, data=None, service_name="aws-external-anthropic", ) headers_without_query = get_auth_headers( method="GET", url="https://gateway.us-east-1.api.aws/v1/models", headers=httpx.Headers({}), aws_access_key="AKIAIOSFODNN7EXAMPLE", aws_secret_key="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", aws_session_token=None, region="us-east-1", profile=None, data=None, service_name="aws-external-anthropic", ) # Different URLs should produce different signatures assert headers_with_query["Authorization"] != headers_without_query["Authorization"] def test_uppercases_method(self) -> None: """Method should be uppercased for signing.""" headers = get_auth_headers( method="post", url="https://gateway.us-east-1.api.aws/v1/messages", headers=httpx.Headers({}), aws_access_key="AKIAIOSFODNN7EXAMPLE", aws_secret_key="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", aws_session_token=None, region="us-east-1", profile=None, data="{}", service_name="aws-external-anthropic", ) assert "Authorization" in headers def test_raises_on_missing_credentials(self) -> None: with patch("anthropic.lib.aws._auth._get_session") as mock_session: mock_session.return_value.get_credentials.return_value = None mock_session.return_value.region_name = "us-east-1" with pytest.raises(RuntimeError, match="Could not resolve AWS credentials"): get_auth_headers( method="POST", url="https://gateway.us-east-1.api.aws/v1/messages", headers=httpx.Headers({}), aws_access_key=None, aws_secret_key=None, aws_session_token=None, region="us-east-1", profile=None, data="{}", service_name="aws-external-anthropic", ) anthropic-sdk-python-0.120.2/tests/lib/test_azure.py000066400000000000000000000230731523216435200224200ustar00rootroot00000000000000from __future__ import annotations import pytest from anthropic._models import FinalRequestOptions from anthropic._exceptions import AnthropicError from anthropic.lib.foundry import AnthropicFoundry, AsyncAnthropicFoundry class TestAnthropicFoundry: def test_basic_initialization_with_api_key(self) -> None: """Test basic client initialization with API key.""" client = AnthropicFoundry( api_key="test-key", resource="example-resource", ) assert client.api_key == "test-key" assert str(client.base_url) == "https://example-resource.services.ai.azure.com/anthropic/" def test_initialization_with_base_url(self) -> None: """Test client initialization with base_url instead of resource.""" client = AnthropicFoundry( api_key="test-key", base_url="https://example.services.ai.azure.com/anthropic/", ) assert str(client.base_url) == "https://example.services.ai.azure.com/anthropic/" def test_initialization_with_azure_ad_token_provider(self) -> None: """Test client initialization with Azure AD token provider.""" def token_provider() -> str: return "test-token" client = AnthropicFoundry( azure_ad_token_provider=token_provider, resource="example-resource", ) assert client._azure_ad_token_provider is not None assert client._get_azure_ad_token() == "test-token" def test_initialization_from_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None: """Test client initialization falls back to environment variables.""" monkeypatch.setenv("ANTHROPIC_FOUNDRY_API_KEY", "env-key") monkeypatch.setenv("ANTHROPIC_API_VERSION", "2023-06-01") monkeypatch.setenv("ANTHROPIC_FOUNDRY_RESOURCE", "env-resource") client = AnthropicFoundry() assert client.api_key == "env-key" assert "env-resource.services.ai.azure.com" in str(client.base_url) def test_missing_credentials_error(self) -> None: """Test error raised when no credentials are provided.""" with pytest.raises(AnthropicError, match="Missing credentials"): AnthropicFoundry( resource="example-resource", ) def test_missing_resource_error(self) -> None: """Test error raised when neither resource nor base_url is provided.""" with pytest.raises(ValueError, match="base_url.*resource"): AnthropicFoundry( api_key="test-key", ) def test_copy(self) -> None: """Test copy() carries over the client configuration.""" def token_provider() -> str: return "test-token" client = AnthropicFoundry( azure_ad_token_provider=token_provider, resource="example-resource", default_headers={"x-app": "1"}, max_retries=5, ) copied = client.copy() assert str(copied.base_url) == str(client.base_url) assert copied._azure_ad_token_provider is token_provider assert copied.default_headers.get("x-app") == "1" assert copied.max_retries == 5 assert copied._client is client._client def test_with_options_overrides(self) -> None: """Test with_options() applies overrides while keeping everything else.""" client = AnthropicFoundry( api_key="test-key", resource="example-resource", default_headers={"x-app": "1"}, ) derived = client.with_options(timeout=10, default_headers={"x-extra": "2"}) assert derived.timeout == 10 assert derived.api_key == "test-key" assert str(derived.base_url) == str(client.base_url) assert derived.default_headers.get("x-app") == "1" assert derived.default_headers.get("x-extra") == "2" def test_copy_x_stainless_helper_header_appends(self) -> None: """x-stainless-helper accumulates across copies instead of being clobbered.""" client = AnthropicFoundry( api_key="test-key", resource="example-resource", default_headers={"x-stainless-helper": "parent"}, ) copied = client.with_options(default_headers={"x-stainless-helper": "child"}) assert copied.default_headers.get("x-stainless-helper") == "parent, child" class TestAsyncAnthropicFoundry: @pytest.mark.asyncio async def test_basic_initialization_with_api_key(self) -> None: """Test basic async client initialization with API key.""" client = AsyncAnthropicFoundry( api_key="test-key", resource="example-resource", ) assert client.api_key == "test-key" assert str(client.base_url) == "https://example-resource.services.ai.azure.com/anthropic/" @pytest.mark.asyncio async def test_async_azure_ad_token_provider(self) -> None: """Test async client with async Azure AD token provider.""" async def async_token_provider() -> str: return "async-test-token" client = AsyncAnthropicFoundry( azure_ad_token_provider=async_token_provider, resource="example-resource", ) token = await client._get_azure_ad_token() assert token == "async-test-token" @pytest.mark.asyncio async def test_initialization_from_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None: """Test async client initialization falls back to environment variables.""" monkeypatch.setenv("ANTHROPIC_FOUNDRY_API_KEY", "env-key") monkeypatch.setenv("ANTHROPIC_API_VERSION", "2023-06-01") monkeypatch.setenv("ANTHROPIC_FOUNDRY_RESOURCE", "env-resource") client = AsyncAnthropicFoundry() assert client.api_key == "env-key" assert "env-resource.services.ai.azure.com" in str(client.base_url) def test_copy(self) -> None: """Test copy() carries over the client configuration.""" async def async_token_provider() -> str: return "async-test-token" client = AsyncAnthropicFoundry( azure_ad_token_provider=async_token_provider, resource="example-resource", max_retries=5, ) copied = client.with_options(timeout=10) assert str(copied.base_url) == str(client.base_url) assert copied._azure_ad_token_provider is async_token_provider assert copied.max_retries == 5 assert copied.timeout == 10 def test_copy_x_stainless_helper_header_appends(self) -> None: """x-stainless-helper accumulates across copies instead of being clobbered.""" client = AsyncAnthropicFoundry( api_key="test-key", resource="example-resource", default_headers={"x-stainless-helper": "parent"}, ) copied = client.with_options(default_headers={"x-stainless-helper": "child"}) assert copied.default_headers.get("x-stainless-helper") == "parent, child" class TestFoundryDoesNotLeakAnthropicAPIKey: """A stray ANTHROPIC_API_KEY in the environment must never be sent to the Foundry endpoint as X-Api-Key, regardless of which auth mode is in use.""" def test_no_x_api_key_with_azure_ad_token_provider(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_API_KEY", "sk-ant-should-not-leak") client = AnthropicFoundry(resource="example-resource", azure_ad_token_provider=lambda: "azure-token") options = client._prepare_options(FinalRequestOptions(method="post", url="/v1/messages")) headers = client._build_request(options).headers assert headers.get("x-api-key") is None assert headers.get("authorization") == "Bearer azure-token" def test_api_key_mode_sends_foundry_key_as_x_api_key(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_API_KEY", "sk-ant-should-not-leak") client = AnthropicFoundry(api_key="foundry-key", resource="example-resource") options = client._prepare_options(FinalRequestOptions(method="post", url="/v1/messages")) headers = client._build_request(options).headers assert headers.get("x-api-key") == "foundry-key" assert headers.get("api-key") == "foundry-key" assert "sk-ant-should-not-leak" not in headers.values() @pytest.mark.asyncio async def test_async_no_x_api_key_with_azure_ad_token_provider(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_API_KEY", "sk-ant-should-not-leak") async def token_provider() -> str: return "azure-token" client = AsyncAnthropicFoundry(resource="example-resource", azure_ad_token_provider=token_provider) options = await client._prepare_options(FinalRequestOptions(method="post", url="/v1/messages")) headers = client._build_request(options).headers assert headers.get("x-api-key") is None assert headers.get("authorization") == "Bearer azure-token" @pytest.mark.asyncio async def test_async_api_key_mode_sends_foundry_key_as_x_api_key(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_API_KEY", "sk-ant-should-not-leak") client = AsyncAnthropicFoundry(api_key="foundry-key", resource="example-resource") options = await client._prepare_options(FinalRequestOptions(method="post", url="/v1/messages")) headers = client._build_request(options).headers assert headers.get("x-api-key") == "foundry-key" assert headers.get("api-key") == "foundry-key" assert "sk-ant-should-not-leak" not in headers.values() anthropic-sdk-python-0.120.2/tests/lib/test_bedrock.py000066400000000000000000000247531523216435200227110ustar00rootroot00000000000000import re import typing as t import tempfile from typing import TypedDict, cast from typing_extensions import Protocol import httpx import pytest from respx import MockRouter from anthropic import AnthropicBedrock, AsyncAnthropicBedrock from anthropic.lib.bedrock._stream_decoder import _chunk_bytes_to_sse sync_client = AnthropicBedrock( aws_region="us-east-1", aws_access_key="example-access-key", aws_secret_key="example-secret-key", ) async_client = AsyncAnthropicBedrock( aws_region="us-east-1", aws_access_key="example-access-key", aws_secret_key="example-secret-key", ) class MockRequestCall(Protocol): request: httpx.Request class AwsConfigProfile(TypedDict): # Available regions: https://docs.aws.amazon.com/global-infrastructure/latest/regions/aws-regions.html#available-regions name: t.Union[t.Literal["default"], str] region: str def profile_to_ini(profile: AwsConfigProfile) -> str: """ Convert an AWS config profile to an INI format string. """ profile_name = profile["name"] if profile["name"] == "default" else f"profile {profile['name']}" return f"[{profile_name}]\nregion = {profile['region']}\n" @pytest.fixture def profiles() -> t.List[AwsConfigProfile]: return [ {"name": "default", "region": "us-east-2"}, ] @pytest.fixture def mock_aws_config( profiles: t.List[AwsConfigProfile], monkeypatch: t.Any, ) -> t.Iterable[None]: with tempfile.NamedTemporaryFile(mode="w+", delete=True) as temp_file: for profile in profiles: temp_file.write(profile_to_ini(profile)) temp_file.flush() monkeypatch.setenv("AWS_CONFIG_FILE", str(temp_file.name)) yield @pytest.mark.filterwarnings("ignore::DeprecationWarning") @pytest.mark.respx() def test_messages_retries(respx_mock: MockRouter) -> None: respx_mock.post(re.compile(r"https://bedrock-runtime\.us-east-1\.amazonaws\.com/model/.*/invoke")).mock( side_effect=[ httpx.Response(500, json={"error": "server error"}, headers={"retry-after-ms": "10"}), httpx.Response(200, json={"foo": "bar"}), ] ) sync_client.messages.create( max_tokens=1024, messages=[ { "role": "user", "content": "Say hello there!", } ], model="anthropic.claude-3-5-sonnet-20241022-v2:0", ) calls = cast("list[MockRequestCall]", respx_mock.calls) assert len(calls) == 2 assert ( calls[0].request.url == "https://bedrock-runtime.us-east-1.amazonaws.com/model/anthropic.claude-3-5-sonnet-20241022-v2:0/invoke" ) assert ( calls[1].request.url == "https://bedrock-runtime.us-east-1.amazonaws.com/model/anthropic.claude-3-5-sonnet-20241022-v2:0/invoke" ) @pytest.mark.filterwarnings("ignore::DeprecationWarning") @pytest.mark.respx() @pytest.mark.asyncio() async def test_messages_retries_async(respx_mock: MockRouter) -> None: respx_mock.post(re.compile(r"https://bedrock-runtime\.us-east-1\.amazonaws\.com/model/.*/invoke")).mock( side_effect=[ httpx.Response(500, json={"error": "server error"}, headers={"retry-after-ms": "10"}), httpx.Response(200, json={"foo": "bar"}), ] ) await async_client.messages.create( max_tokens=1024, messages=[ { "role": "user", "content": "Say hello there!", } ], model="anthropic.claude-3-5-sonnet-20241022-v2:0", ) calls = cast("list[MockRequestCall]", respx_mock.calls) assert len(calls) == 2 assert ( calls[0].request.url == "https://bedrock-runtime.us-east-1.amazonaws.com/model/anthropic.claude-3-5-sonnet-20241022-v2:0/invoke" ) assert ( calls[1].request.url == "https://bedrock-runtime.us-east-1.amazonaws.com/model/anthropic.claude-3-5-sonnet-20241022-v2:0/invoke" ) @pytest.mark.filterwarnings("ignore::DeprecationWarning") @pytest.mark.respx() def test_application_inference_profile(respx_mock: MockRouter) -> None: respx_mock.post(re.compile(r"https://bedrock-runtime\.us-east-1\.amazonaws\.com/model/.*/invoke")).mock( side_effect=[ httpx.Response(500, json={"error": "server error"}, headers={"retry-after-ms": "10"}), httpx.Response(200, json={"foo": "bar"}), ] ) sync_client.messages.create( max_tokens=1024, messages=[ { "role": "user", "content": "Say hello there!", } ], model="arn:aws:bedrock:us-east-1:123456789012:application-inference-profile/jf2sje1c0jnb", ) calls = cast("list[MockRequestCall]", respx_mock.calls) assert len(calls) == 2 assert ( calls[0].request.url == "https://bedrock-runtime.us-east-1.amazonaws.com/model/arn:aws:bedrock:us-east-1:123456789012:application-inference-profile%2Fjf2sje1c0jnb/invoke" ) assert ( calls[1].request.url == "https://bedrock-runtime.us-east-1.amazonaws.com/model/arn:aws:bedrock:us-east-1:123456789012:application-inference-profile%2Fjf2sje1c0jnb/invoke" ) sync_api_key_client = AnthropicBedrock( aws_region="us-east-1", api_key="test-api-key", ) async_api_key_client = AsyncAnthropicBedrock( aws_region="us-east-1", api_key="test-api-key", ) @pytest.mark.filterwarnings("ignore::DeprecationWarning") @pytest.mark.respx() def test_api_key_auth(respx_mock: MockRouter) -> None: respx_mock.post(re.compile(r"https://bedrock-runtime\.us-east-1\.amazonaws\.com/model/.*/invoke")).mock( return_value=httpx.Response(200, json={"foo": "bar"}), ) sync_api_key_client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Say hello there!"}], model="anthropic.claude-3-5-sonnet-20241022-v2:0", ) calls = cast("list[MockRequestCall]", respx_mock.calls) assert len(calls) == 1 assert calls[0].request.headers["Authorization"] == "Bearer test-api-key" @pytest.mark.filterwarnings("ignore::DeprecationWarning") @pytest.mark.respx() @pytest.mark.asyncio() async def test_api_key_auth_async(respx_mock: MockRouter) -> None: respx_mock.post(re.compile(r"https://bedrock-runtime\.us-east-1\.amazonaws\.com/model/.*/invoke")).mock( return_value=httpx.Response(200, json={"foo": "bar"}), ) await async_api_key_client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Say hello there!"}], model="anthropic.claude-3-5-sonnet-20241022-v2:0", ) calls = cast("list[MockRequestCall]", respx_mock.calls) assert len(calls) == 1 assert calls[0].request.headers["Authorization"] == "Bearer test-api-key" def test_api_key_from_env(monkeypatch: t.Any) -> None: monkeypatch.setenv("AWS_BEARER_TOKEN_BEDROCK", "env-api-key") client = AnthropicBedrock(aws_region="us-east-1") assert client.api_key == "env-api-key" def test_api_key_mutual_exclusion() -> None: with pytest.raises(ValueError, match="Cannot specify both"): AnthropicBedrock( aws_region="us-east-1", api_key="test-api-key", aws_access_key="example-access-key", ) def test_api_key_mutual_exclusion_async() -> None: with pytest.raises(ValueError, match="Cannot specify both"): AsyncAnthropicBedrock( aws_region="us-east-1", api_key="test-api-key", aws_secret_key="example-secret-key", ) def test_api_key_env_mutual_exclusion(monkeypatch: t.Any) -> None: monkeypatch.setenv("AWS_BEARER_TOKEN_BEDROCK", "env-api-key") with pytest.raises(ValueError, match="Cannot specify both"): AnthropicBedrock( aws_region="us-east-1", aws_access_key="example-access-key", ) def test_region_infer_from_profile( mock_aws_config: None, # noqa: ARG001 profiles: t.List[AwsConfigProfile], ) -> None: client = AnthropicBedrock() assert client.aws_region == profiles[0]["region"] @pytest.mark.parametrize( "profiles, aws_profile", [ pytest.param([{"name": "default", "region": "us-east-2"}], "default", id="default profile"), pytest.param( [{"name": "default", "region": "us-east-2"}, {"name": "custom", "region": "us-west-1"}], "custom", id="custom profile", ), ], ) def test_region_infer_from_specified_profile( mock_aws_config: None, # noqa: ARG001 profiles: t.List[AwsConfigProfile], aws_profile: str, monkeypatch: t.Any, ) -> None: monkeypatch.setenv("AWS_PROFILE", aws_profile) client = AnthropicBedrock() assert client.aws_region == next(profile for profile in profiles if profile["name"] == aws_profile)["region"] def test_chunk_bytes_to_sse_typed_event() -> None: raw = ( b'{"type":"message_start","message":{"id":"msg_123","type":"message","role":"assistant",' b'"content":[],"model":"claude-x","stop_reason":null,"stop_sequence":null,' b'"usage":{"input_tokens":1,"output_tokens":1}}}' ) sse = _chunk_bytes_to_sse(raw) assert sse is not None assert sse.event == "message_start" assert sse.data == raw.decode() def test_chunk_bytes_to_sse_legacy_completion() -> None: raw = b'{"completion":" Hello","stop_reason":null,"model":"claude-2"}' sse = _chunk_bytes_to_sse(raw) assert sse is not None assert sse.event == "completion" def test_chunk_bytes_to_sse_legacy_completion_with_metrics() -> None: raw = ( b'{"completion":" Hello","stop_reason":"stop_sequence","model":"claude-2",' b'"amazon-bedrock-invocationMetrics":{"inputTokenCount":1,"outputTokenCount":1}}' ) sse = _chunk_bytes_to_sse(raw) assert sse is not None assert sse.event == "completion" def test_copy_x_stainless_helper_header_appends() -> None: # `x-stainless-helper` accumulates across copies instead of being clobbered client = sync_client.with_options(default_headers={"x-stainless-helper": "parent"}) copied = client.with_options(default_headers={"x-stainless-helper": "child"}) assert copied.default_headers["x-stainless-helper"] == "parent, child" def test_async_copy_x_stainless_helper_header_appends() -> None: # `x-stainless-helper` accumulates across copies instead of being clobbered client = async_client.with_options(default_headers={"x-stainless-helper": "parent"}) copied = client.with_options(default_headers={"x-stainless-helper": "child"}) assert copied.default_headers["x-stainless-helper"] == "parent, child" anthropic-sdk-python-0.120.2/tests/lib/test_bedrock_mantle.py000066400000000000000000000243621523216435200242450ustar00rootroot00000000000000from __future__ import annotations from unittest.mock import MagicMock, patch import httpx import pytest from anthropic import AnthropicBedrockMantle, AsyncAnthropicBedrockMantle class TestBaseURL: def test_derives_base_url_from_region(self) -> None: client = AnthropicBedrockMantle( api_key="test-key", aws_region="us-east-1", ) assert str(client.base_url).startswith("https://bedrock-mantle.us-east-1.api.aws/anthropic") def test_different_region(self) -> None: client = AnthropicBedrockMantle( api_key="test-key", aws_region="us-west-2", ) assert str(client.base_url).startswith("https://bedrock-mantle.us-west-2.api.aws/anthropic") def test_uses_base_url_env_var(self) -> None: with patch.dict("os.environ", {"ANTHROPIC_BEDROCK_MANTLE_BASE_URL": "https://custom.example.com"}): client = AnthropicBedrockMantle( api_key="test-key", ) assert str(client.base_url).startswith("https://custom.example.com") def test_base_url_arg_takes_precedence_over_env(self) -> None: with patch.dict("os.environ", {"ANTHROPIC_BEDROCK_MANTLE_BASE_URL": "https://from-env.example.com"}): client = AnthropicBedrockMantle( api_key="test-key", base_url="https://from-arg.example.com", ) assert str(client.base_url).startswith("https://from-arg.example.com") def test_raises_without_region_or_base_url(self) -> None: with pytest.raises(Exception, match="No AWS region or base URL found"): AnthropicBedrockMantle( api_key="test-key", ) class TestSigV4ServiceName: def test_uses_bedrock_mantle_service_name(self) -> None: client = AnthropicBedrockMantle( aws_access_key="AKID", aws_secret_key="secret", aws_region="us-east-1", ) mock_request = MagicMock(spec=httpx.Request) mock_request.method = "POST" mock_request.url = httpx.URL("https://bedrock-mantle.us-east-1.api.aws/anthropic/v1/messages") mock_request.headers = httpx.Headers({"content-type": "application/json"}) mock_request.read.return_value = b'{"model": "claude-sonnet-4-20250514"}' with patch("anthropic.lib.bedrock._mantle.get_auth_headers") as mock_auth: mock_auth.return_value = { "Authorization": "AWS4-HMAC-SHA256 ...", "X-Amz-Date": "20260327T000000Z", } client._prepare_request(mock_request) mock_auth.assert_called_once() call_kwargs = mock_auth.call_args.kwargs assert call_kwargs["service_name"] == "bedrock-mantle" class TestEnvironmentVariables: def test_uses_mantle_api_key_env_var(self) -> None: with patch.dict("os.environ", {"AWS_BEARER_TOKEN_BEDROCK": "mantle-key"}, clear=False): client = AnthropicBedrockMantle( base_url="https://example.com", ) assert client.api_key == "mantle-key" def test_falls_back_to_aws_api_key_env_var(self) -> None: with patch.dict("os.environ", {"ANTHROPIC_AWS_API_KEY": "aws-key"}, clear=False): client = AnthropicBedrockMantle( base_url="https://example.com", ) assert client.api_key == "aws-key" def test_mantle_api_key_takes_precedence_over_aws(self) -> None: with patch.dict( "os.environ", { "AWS_BEARER_TOKEN_BEDROCK": "mantle-key", "ANTHROPIC_AWS_API_KEY": "aws-key", }, clear=False, ): client = AnthropicBedrockMantle( base_url="https://example.com", ) assert client.api_key == "mantle-key" def test_region_from_aws_region_env_var(self) -> None: with patch.dict("os.environ", {"AWS_REGION": "eu-west-1"}, clear=False): client = AnthropicBedrockMantle( api_key="test-key", ) assert client.aws_region == "eu-west-1" assert client.base_url == "https://bedrock-mantle.eu-west-1.api.aws/anthropic/" def test_region_from_aws_default_region_env_var(self) -> None: with patch.dict("os.environ", {"AWS_DEFAULT_REGION": "ap-southeast-1"}, clear=False): client = AnthropicBedrockMantle( api_key="test-key", ) assert client.aws_region == "ap-southeast-1" class TestEndpointRestrictions: def _make_client(self) -> AnthropicBedrockMantle: return AnthropicBedrockMantle( api_key="test-key", base_url="https://example.com", ) def test_completions_not_available(self) -> None: client = self._make_client() assert not hasattr(client, "completions") def test_models_not_available(self) -> None: client = self._make_client() assert not hasattr(client, "models") def test_messages_available(self) -> None: client = self._make_client() assert client.messages is not None def test_beta_messages_available(self) -> None: client = self._make_client() assert client.beta.messages is not None def test_beta_models_not_available(self) -> None: client = self._make_client() assert not hasattr(client.beta, "models") def test_beta_files_not_available(self) -> None: client = self._make_client() assert not hasattr(client.beta, "files") def test_beta_skills_not_available(self) -> None: client = self._make_client() assert not hasattr(client.beta, "skills") class TestAuthPrecedence: def test_api_key_arg_uses_api_key_mode(self) -> None: client = AnthropicBedrockMantle( api_key="my-key", aws_region="us-east-1", ) assert client._use_sigv4 is False assert client.api_key == "my-key" def test_aws_creds_use_sigv4_mode(self) -> None: client = AnthropicBedrockMantle( aws_access_key="AKID", aws_secret_key="secret", aws_region="us-east-1", ) assert client._use_sigv4 is True assert client.api_key is None def test_api_key_mode_returns_bearer_auth_header(self) -> None: client = AnthropicBedrockMantle( api_key="my-key", aws_region="us-east-1", ) assert client.auth_headers == {"Authorization": "Bearer my-key"} def test_sigv4_mode_returns_empty_auth_headers(self) -> None: client = AnthropicBedrockMantle( aws_access_key="AKID", aws_secret_key="secret", aws_region="us-east-1", ) assert client.auth_headers == {} def test_skip_auth_returns_empty_auth_headers(self) -> None: client = AnthropicBedrockMantle( skip_auth=True, base_url="https://example.com", ) assert client.auth_headers == {} class TestSkipAuth: def test_skip_auth_does_not_sign_request(self) -> None: client = AnthropicBedrockMantle( skip_auth=True, base_url="https://example.com", ) mock_request = MagicMock(spec=httpx.Request) with patch("anthropic.lib.bedrock._mantle.get_auth_headers") as mock_auth: client._prepare_request(mock_request) mock_auth.assert_not_called() class TestPartialCredentials: def test_access_key_only_raises(self) -> None: with pytest.raises(ValueError, match="aws_access_key.*without.*aws_secret_key"): AnthropicBedrockMantle( aws_access_key="AKID", aws_region="us-east-1", ) def test_secret_key_only_raises(self) -> None: with pytest.raises(ValueError, match="aws_secret_key.*without.*aws_access_key"): AnthropicBedrockMantle( aws_secret_key="secret", aws_region="us-east-1", ) class TestAsyncClient: def test_async_client_has_same_restrictions(self) -> None: client = AsyncAnthropicBedrockMantle( api_key="test-key", base_url="https://example.com", ) assert not hasattr(client, "completions") assert not hasattr(client, "models") assert client.messages is not None assert client.beta.messages is not None assert not hasattr(client.beta, "models") assert not hasattr(client.beta, "files") assert not hasattr(client.beta, "skills") def test_async_base_url_from_region(self) -> None: client = AsyncAnthropicBedrockMantle( api_key="test-key", aws_region="us-east-1", ) assert client.base_url == "https://bedrock-mantle.us-east-1.api.aws/anthropic/" class TestCopy: def test_copy_preserves_config(self) -> None: client = AnthropicBedrockMantle( api_key="test-key", aws_region="us-east-1", ) copied = client.copy() assert copied.base_url == client.base_url assert copied.aws_region == client.aws_region def test_copy_overrides_region(self) -> None: client = AnthropicBedrockMantle( api_key="test-key", aws_region="us-east-1", ) copied = client.copy(aws_region="us-west-2") assert copied.aws_region == "us-west-2" def test_copy_x_stainless_helper_header_appends(self) -> None: # `x-stainless-helper` accumulates across copies instead of being clobbered client = AnthropicBedrockMantle( api_key="test-key", aws_region="us-east-1", default_headers={"x-stainless-helper": "parent"}, ) copied = client.copy(default_headers={"x-stainless-helper": "child"}) assert copied.default_headers["x-stainless-helper"] == "parent, child" def test_async_copy_x_stainless_helper_header_appends(self) -> None: # `x-stainless-helper` accumulates across copies instead of being clobbered client = AsyncAnthropicBedrockMantle( api_key="test-key", aws_region="us-east-1", default_headers={"x-stainless-helper": "parent"}, ) copied = client.copy(default_headers={"x-stainless-helper": "child"}) assert copied.default_headers["x-stainless-helper"] == "parent, child" anthropic-sdk-python-0.120.2/tests/lib/test_credentials.py000066400000000000000000004757141523216435200236040ustar00rootroot00000000000000from __future__ import annotations import os import json import time import logging import pathlib from typing import Any, Dict, List, Callable, Optional, cast from typing_extensions import Protocol import httpx import pytest from respx import MockRouter import anthropic from anthropic import ( EnvToken, Anthropic, TokenCache, AccessToken, StaticToken, AnthropicError, AsyncAnthropic, InMemoryConfig, CredentialsFile, IdentityTokenFile, WorkloadIdentityError, WorkloadIdentityCredentials, default_credentials, exchange_federation_assertion, ) from anthropic._version import __version__ from anthropic._base_client import FinalRequestOptions from anthropic.lib.credentials._constants import ( TOKEN_ENDPOINT, GRANT_TYPE_JWT_BEARER, OAUTH_API_BETA_HEADER, FEDERATION_BETA_HEADER, ) BASE_URL = "https://api.anthropic.com" TOKEN_URL = f"{BASE_URL}{TOKEN_ENDPOINT}" _ALL_ENV = [ "ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN", "ANTHROPIC_CONFIG_DIR", "ANTHROPIC_PROFILE", "ANTHROPIC_IDENTITY_TOKEN", "ANTHROPIC_IDENTITY_TOKEN_FILE", "ANTHROPIC_FEDERATION_RULE_ID", "ANTHROPIC_ORGANIZATION_ID", "ANTHROPIC_SERVICE_ACCOUNT_ID", "ANTHROPIC_WORKSPACE_ID", "ANTHROPIC_SCOPE", ] class MockRequestCall(Protocol): request: httpx.Request @pytest.fixture def clean_env(monkeypatch: pytest.MonkeyPatch) -> pytest.MonkeyPatch: for var in _ALL_ENV: monkeypatch.delenv(var, raising=False) return monkeypatch @pytest.fixture def no_default_creds_file(monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path) -> None: """Point the config directory at an empty location so a real ~/.config/anthropic/ on the dev machine doesn't leak into tests. Patches ``_config_dir`` directly rather than setting ``ANTHROPIC_CONFIG_DIR`` so that ``clean_env`` (which deletes that env var) can't clobber the isolation. """ empty = tmp_path / "empty-config-dir" empty.mkdir() monkeypatch.setattr("anthropic.lib.credentials._constants._config_dir", lambda: empty) # Field names that live at the top level of the new nested config shape # (outside the ``authentication`` object). _TOP_LEVEL_CONFIG_KEYS = {"base_url", "organization_id", "workspace_id"} def _migrate_legacy_config(flat: Dict[str, Any]) -> Dict[str, Any]: """Adapter: convert a flat legacy config dict into the new nested shape. Many tests in this file predate the schema migration and pass legacy flat configs like ``{"type": "workload_identity", "federation_rule_id": ...}``. Rather than churn every caller, this helper translates at the test-helper layer — tests that want to assert against the new shape directly can pass a config dict that already contains an ``"authentication"`` key. """ result: Dict[str, Any] = {} auth: Dict[str, Any] = {} for key, value in flat.items(): if key == "type": continue if key in _TOP_LEVEL_CONFIG_KEYS: result[key] = value else: auth[key] = value # "external" used to mean "token already in credentials file, no # refresh" — the new schema expresses this as user_oauth without a # client_id (the key "external → user_oauth" both preserves the daemon # semantics and keeps auth-only fields like credentials_path intact). # "workload_identity" / "authorized_user" were renamed. auth["type"] = { "external": "user_oauth", "workload_identity": "oidc_federation", "authorized_user": "user_oauth", }.get(flat["type"], flat["type"]) result["authentication"] = auth return result def _write_profile( config_dir: pathlib.Path, profile: str, config: Dict[str, Any], credentials: Optional[Dict[str, Any]] = None, ) -> None: """Test helper: lay out ``configs/.json`` and optionally ``credentials/.json`` under ``config_dir``. Accepts either the new nested ``{"authentication": {...}}`` shape or a legacy flat shape (``{"type": "workload_identity", ...}``) for backwards compatibility with the tests that predate the schema migration. Legacy inputs are translated via :func:`_migrate_legacy_config` before being written to disk. Prepends ``"type": "oauth_token"`` to the credentials dict unless the caller already supplied a ``type`` key (so negative tests can override). """ if "type" in config and "authentication" not in config: config = _migrate_legacy_config(config) (config_dir / "configs").mkdir(parents=True, exist_ok=True) (config_dir / "configs" / f"{profile}.json").write_text(json.dumps(config)) if credentials is not None: if "type" not in credentials: credentials = {"type": "oauth_token", **credentials} (config_dir / "credentials").mkdir(parents=True, exist_ok=True) creds_path = config_dir / "credentials" / f"{profile}.json" creds_path.write_text(json.dumps(credentials)) # Match the 0o600 invariant the real credentials reader now enforces. creds_path.chmod(0o600) # --------------------------------------------------------------------------- # # Basic providers # --------------------------------------------------------------------------- # class TestAccessToken: def test_defaults(self) -> None: tok = AccessToken("abc") assert tok.token == "abc" assert tok.expires_at is None def test_with_expiry(self) -> None: tok = AccessToken("abc", expires_at=123) assert tok.expires_at == 123 class TestStaticToken: def test_returns_token(self) -> None: p = StaticToken("sk-ant-oat01-static") assert p() == AccessToken("sk-ant-oat01-static", None) assert p().expires_at is None class TestEnvToken: def test_reads_env(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_AUTH_TOKEN", "env-token") assert EnvToken()().token == "env-token" def test_raises_when_missing(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.delenv("ANTHROPIC_AUTH_TOKEN", raising=False) with pytest.raises(AnthropicError, match="ANTHROPIC_AUTH_TOKEN"): EnvToken()() class TestIdentityTokenFile: def test_rereads_on_each_call(self, tmp_path: pathlib.Path) -> None: f = tmp_path / "token" f.write_text("jwt-one\n") provider = IdentityTokenFile(f) assert provider() == "jwt-one" f.write_text("jwt-two\n") assert provider() == "jwt-two" def test_reads_env_var_path(self, tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch) -> None: f = tmp_path / "token" f.write_text("from-env") monkeypatch.setenv("ANTHROPIC_IDENTITY_TOKEN_FILE", str(f)) assert IdentityTokenFile()() == "from-env" def test_raises_when_no_path(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.delenv("ANTHROPIC_IDENTITY_TOKEN_FILE", raising=False) with pytest.raises(AnthropicError, match="ANTHROPIC_IDENTITY_TOKEN_FILE"): IdentityTokenFile() def test_raises_when_file_missing(self, tmp_path: pathlib.Path) -> None: with pytest.raises(AnthropicError, match="not found"): IdentityTokenFile(tmp_path / "nope")() class TestCredentialsFile: """All tests use ``ANTHROPIC_CONFIG_DIR`` to point at a tmp directory laid out as ``configs/.json`` + ``credentials/.json``.""" @pytest.fixture(autouse=True) def _isolate(self, monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path) -> None: for var in _ALL_ENV: monkeypatch.delenv(var, raising=False) monkeypatch.setenv("ANTHROPIC_CONFIG_DIR", str(tmp_path)) # -- "type": "external" ------------------------------------------------ def test_external(self, tmp_path: pathlib.Path) -> None: _write_profile( tmp_path, "default", config={"type": "external"}, credentials={"access_token": "sk-ant-oat01-x", "expires_at": 1775000000}, ) tok = CredentialsFile()() assert tok.token == "sk-ant-oat01-x" assert tok.expires_at == 1775000000 def test_external_no_expiry(self, tmp_path: pathlib.Path) -> None: _write_profile(tmp_path, "default", {"type": "external"}, {"access_token": "sk-ant-oat01-x"}) assert CredentialsFile()().expires_at is None def test_external_rereads_credentials_on_each_call(self, tmp_path: pathlib.Path) -> None: """An external process rotates the credentials file; config stays fixed.""" _write_profile(tmp_path, "default", {"type": "external"}, {"access_token": "first"}) provider = CredentialsFile() assert provider().token == "first" # External rotation: rewrite credentials only. (tmp_path / "credentials" / "default.json").write_text(json.dumps({"access_token": "second"})) assert provider().token == "second" def test_external_missing_credentials_file(self, tmp_path: pathlib.Path) -> None: _write_profile(tmp_path, "default", {"type": "external"}) # no credentials file with pytest.raises(AnthropicError, match="Credentials file not found"): CredentialsFile()() def test_credentials_file_wrong_type_raises(self, tmp_path: pathlib.Path) -> None: _write_profile( tmp_path, "default", config={"type": "external"}, credentials={"type": "something_else", "access_token": "x"}, ) with pytest.raises( AnthropicError, match="credentials file has type 'something_else'; expected 'oauth_token' for authentication.type 'user_oauth'", ): CredentialsFile()() def test_credentials_file_absent_type_is_lenient(self, tmp_path: pathlib.Path) -> None: """Hand-written credentials files without ``type`` are accepted.""" _write_profile(tmp_path, "default", {"type": "external"}) # Write credentials directly (bypass helper's type injection). (tmp_path / "credentials").mkdir(exist_ok=True) creds_path = tmp_path / "credentials" / "default.json" creds_path.write_text(json.dumps({"access_token": "no-type-field"})) creds_path.chmod(0o600) assert CredentialsFile()().token == "no-type-field" def test_unrecognized_top_level_keys_ignored(self, tmp_path: pathlib.Path) -> None: """Unknown top-level keys in both files are silently ignored (forward compat).""" _write_profile( tmp_path, "default", config={ "type": "external", "_note": "test comment", "future_field": 123, "nested_future": {"a": [1, 2]}, }, credentials={ "access_token": "tolerant", "expires_at": 1775000000, "_note": "creds comment", "future_field": 123, }, ) tok = CredentialsFile()() assert tok.token == "tolerant" assert tok.expires_at == 1775000000 # -- "type": "workload_identity" -------------------------------------- @pytest.mark.respx(base_url=BASE_URL) def test_workload_identity_dispatch(self, respx_mock: MockRouter, tmp_path: pathlib.Path) -> None: jwt_path = tmp_path / "jwt" jwt_path.write_text("ext-jwt-from-file") # workload_identity needs no credentials file — config is sufficient. _write_profile( tmp_path, "default", config={ "type": "workload_identity", "identity_token": {"source": "file", "path": str(jwt_path)}, "federation_rule_id": "fdrl_file", "organization_id": "org-from-file", "service_account_id": "svac_file", }, ) token_route = respx_mock.post(TOKEN_ENDPOINT).mock( return_value=httpx.Response(200, json={"access_token": "exch_tok", "expires_in": 3600}) ) provider = CredentialsFile() tok = provider() assert tok.token == "exch_tok" assert tok.expires_at is not None and tok.expires_at > time.time() assert token_route.call_count == 1 body = json.loads(cast("list[MockRequestCall]", token_route.calls)[0].request.content) assert body["grant_type"] == GRANT_TYPE_JWT_BEARER assert body["assertion"] == "ext-jwt-from-file" assert body["federation_rule_id"] == "fdrl_file" assert body["organization_id"] == "org-from-file" assert body["service_account_id"] == "svac_file" assert "workspace_id" not in body assert "scope" not in body # Disk cache: the exchange wrote credentials/.json with 0600 # perms, so the second call returns the cached token without hitting # the network. Delegate is still cached on the provider. provider() assert token_route.call_count == 1 assert provider._workload_delegate is not None # pyright: ignore[reportPrivateUsage] cached = json.loads((tmp_path / "credentials" / "default.json").read_text()) assert cached["version"] == "1.0" assert cached["type"] == "oauth_token" assert cached["access_token"] == "exch_tok" assert isinstance(cached["expires_at"], int) @pytest.mark.respx() def test_bind_base_url_precedence(self, respx_mock: MockRouter, tmp_path: pathlib.Path) -> None: """``bind_base_url`` slots between the config file's own ``base_url`` field and the hard-coded default: config → bound → default.""" jwt_path = tmp_path / "jwt" jwt_path.write_text("j") bound = "https://bound.example" def write(profile: str, *, with_base_url: Optional[str]) -> None: cfg: Dict[str, Any] = { "type": "workload_identity", "identity_token": {"source": "file", "path": str(jwt_path)}, "federation_rule_id": "fdrl_x", "organization_id": "org", } if with_base_url is not None: cfg["base_url"] = with_base_url _write_profile(tmp_path, profile, config=cfg) # config omits base_url, no bind → DEFAULT_BASE_URL write("p0", with_base_url=None) respx_mock.post(TOKEN_URL).mock(return_value=httpx.Response(200, json={"access_token": "t", "expires_in": 60})) CredentialsFile("p0")() assert str(cast("list[MockRequestCall]", respx_mock.calls)[-1].request.url) == TOKEN_URL # config omits base_url + bound → bound write("p1", with_base_url=None) respx_mock.post(f"{bound}{TOKEN_ENDPOINT}").mock( return_value=httpx.Response(200, json={"access_token": "t", "expires_in": 60}) ) creds = CredentialsFile("p1") creds.bind_base_url(bound) creds() assert str(cast("list[MockRequestCall]", respx_mock.calls)[-1].request.url) == f"{bound}{TOKEN_ENDPOINT}" # config has base_url + bound → config wins write("p2", with_base_url="https://from-config.example") respx_mock.post(f"https://from-config.example{TOKEN_ENDPOINT}").mock( return_value=httpx.Response(200, json={"access_token": "t", "expires_in": 60}) ) creds = CredentialsFile("p2") creds.bind_base_url(bound) creds() assert ( str(cast("list[MockRequestCall]", respx_mock.calls)[-1].request.url) == f"https://from-config.example{TOKEN_ENDPOINT}" ) # re-entrancy: bind_base_url called *after* _load_config() re-resolves # in place — the last bind wins when the config file doesn't pin a host. write("p3", with_base_url=None) creds = CredentialsFile("p3") creds.extra_headers() # forces _load_config() creds.bind_base_url("https://first.example") creds.bind_base_url("https://second.example") assert creds._base_url == "https://second.example" # type: ignore[attr-defined] # eager scheme validation — http:// rejected at bind time, before load with pytest.raises(AnthropicError, match="https"): CredentialsFile("p3").bind_base_url("http://evil.example") @pytest.mark.respx(base_url=BASE_URL) def test_workload_identity_disk_cache_stale_reexchange( self, respx_mock: MockRouter, tmp_path: pathlib.Path ) -> None: """A stale on-disk credentials file is ignored — fresh exchange happens and the file is rewritten.""" jwt_path = tmp_path / "jwt" jwt_path.write_text("ext-jwt") _write_profile( tmp_path, "default", config={ "type": "workload_identity", "identity_token": {"source": "file", "path": str(jwt_path)}, "federation_rule_id": "fdrl_x", "organization_id": "org_x", }, credentials={"access_token": "stale-tok", "expires_at": int(time.time()) - 1}, ) token_route = respx_mock.post(TOKEN_ENDPOINT).mock( return_value=httpx.Response(200, json={"access_token": "fresh-tok", "expires_in": 600}) ) tok = CredentialsFile()() assert tok.token == "fresh-tok" assert token_route.call_count == 1 rewritten = json.loads((tmp_path / "credentials" / "default.json").read_text()) assert rewritten["access_token"] == "fresh-tok" @pytest.mark.respx(base_url=BASE_URL) def test_workload_identity_disk_cache_corrupt_expires_at_reexchange( self, respx_mock: MockRouter, tmp_path: pathlib.Path ) -> None: """A non-numeric expires_at in the on-disk credentials file falls through to re-exchange instead of raising.""" jwt_path = tmp_path / "jwt" jwt_path.write_text("ext-jwt") _write_profile( tmp_path, "default", config={ "type": "workload_identity", "identity_token": {"source": "file", "path": str(jwt_path)}, "federation_rule_id": "fdrl_x", "organization_id": "org_x", }, credentials={"access_token": "stale-tok", "expires_at": "not-a-number"}, ) token_route = respx_mock.post(TOKEN_ENDPOINT).mock( return_value=httpx.Response(200, json={"access_token": "fresh-tok", "expires_in": 600}) ) tok = CredentialsFile()() assert tok.token == "fresh-tok" assert token_route.call_count == 1 def test_workload_identity_missing_required_fields(self, tmp_path: pathlib.Path) -> None: _write_profile(tmp_path, "default", {"type": "workload_identity", "federation_rule_id": "fdrl_x"}) with pytest.raises( WorkloadIdentityError, match="'authentication.federation_rule_id' and top-level 'organization_id'" ): CredentialsFile()() @pytest.mark.respx(base_url=BASE_URL) def test_workload_delegate_borrows_parent_http_client(self, respx_mock: MockRouter, tmp_path: pathlib.Path) -> None: """The workload delegate must borrow CredentialsFile's owned httpx.Client rather than creating its own. CredentialsFile.close() then has a single client to release; if a refactor regresses this and the delegate creates its own pool, this test catches it before close() starts leaking sockets.""" jwt_path = tmp_path / "jwt" jwt_path.write_text("ext-jwt") _write_profile( tmp_path, "default", config={ "type": "workload_identity", "identity_token": {"source": "file", "path": str(jwt_path)}, "federation_rule_id": "fdrl_x", "organization_id": "00000000-0000-0000-0000-000000000000", }, ) respx_mock.post(TOKEN_ENDPOINT).mock( return_value=httpx.Response(200, json={"access_token": "t", "expires_in": 60}) ) provider = CredentialsFile() provider() delegate = provider._workload_delegate # pyright: ignore[reportPrivateUsage] assert delegate is not None # The delegate must NOT own its httpx.Client — that would mean we have # two pools to track and close(). assert delegate._owns_http_client is False # pyright: ignore[reportPrivateUsage] # Both objects share the same client instance. assert delegate._http_client is provider._get_http_client() # pyright: ignore[reportPrivateUsage] provider.close() def test_workload_identity_unknown_source_raises(self, tmp_path: pathlib.Path) -> None: _write_profile( tmp_path, "default", { "type": "workload_identity", "federation_rule_id": "f", "organization_id": "o", "identity_token": {"source": "url", "url": "https://example.com/token"}, }, ) with pytest.raises(AnthropicError, match="identity_token source 'url' is not supported"): CredentialsFile()() def test_workload_identity_token_omitted_uses_env( self, tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch ) -> None: jwt_path = tmp_path / "jwt" jwt_path.write_text("via-env-chain") monkeypatch.setenv("ANTHROPIC_IDENTITY_TOKEN_FILE", str(jwt_path)) _write_profile( tmp_path, "default", {"type": "workload_identity", "federation_rule_id": "f", "organization_id": "o"} ) provider = CredentialsFile() # Building the delegate succeeds (provider resolved via env); we don't # need to exercise the HTTP call here. provider._load_config() # pyright: ignore[reportPrivateUsage] delegate = provider._build_workload_delegate( # pyright: ignore[reportPrivateUsage] provider._auth_block() # pyright: ignore[reportPrivateUsage] ) assert delegate._identity_token_provider() == "via-env-chain" # pyright: ignore[reportPrivateUsage] # -- "type": "authorized_user" ---------------------------------------- @pytest.mark.respx(base_url=BASE_URL) def test_authorized_user_refresh_and_writeback(self, respx_mock: MockRouter, tmp_path: pathlib.Path) -> None: """Refresh writes back to credentials/ only — configs/ stays untouched.""" _write_profile( tmp_path, "default", config={"type": "authorized_user", "client_id": "cid", "scope": "x:y"}, credentials={ "access_token": "old-tok", "expires_at": int(time.time()) - 1, "refresh_token": "refresh-old", }, ) refresh_route = respx_mock.post(TOKEN_ENDPOINT).mock( return_value=httpx.Response( 200, json={"access_token": "new-tok", "expires_in": 3600, "refresh_token": "refresh-new"} ) ) tok = CredentialsFile()() assert tok.token == "new-tok" assert tok.expires_at is not None and tok.expires_at > time.time() assert refresh_route.call_count == 1 body = json.loads(cast("list[MockRequestCall]", refresh_route.calls)[0].request.content) assert body == {"grant_type": "refresh_token", "refresh_token": "refresh-old", "client_id": "cid"} # Credentials file was rewritten — expires_at is unix int seconds creds_file = tmp_path / "credentials" / "default.json" rewritten = json.loads(creds_file.read_text()) assert rewritten["version"] == "1.0" assert rewritten["type"] == "oauth_token" assert rewritten["access_token"] == "new-tok" assert rewritten["refresh_token"] == "refresh-new" assert isinstance(rewritten["expires_at"], int) assert rewritten["expires_at"] > time.time() assert not creds_file.with_suffix(".json.tmp").exists() # Config file was NOT touched config_after = json.loads((tmp_path / "configs" / "default.json").read_text()) assert config_after == { "authentication": { "type": "user_oauth", "client_id": "cid", "scope": "x:y", } } def test_authorized_user_fresh_token_no_refresh(self, tmp_path: pathlib.Path) -> None: _write_profile( tmp_path, "default", config={"type": "authorized_user"}, credentials={ "access_token": "still-good", "expires_at": int(time.time()) + 3600, "refresh_token": "refresh-old", }, ) tok = CredentialsFile()() assert tok.token == "still-good" @pytest.mark.respx(base_url=BASE_URL) def test_authorized_user_refresh_failure(self, respx_mock: MockRouter, tmp_path: pathlib.Path) -> None: _write_profile( tmp_path, "default", config={"type": "authorized_user", "client_id": "cid"}, credentials={"access_token": "old", "expires_at": int(time.time()) - 1, "refresh_token": "rt"}, ) respx_mock.post(TOKEN_ENDPOINT).mock(return_value=httpx.Response(400, json={"error": "invalid_grant"})) with pytest.raises(WorkloadIdentityError, match="refresh failed"): CredentialsFile()() def test_user_oauth_without_client_id_is_static(self, tmp_path: pathlib.Path) -> None: """user_oauth without a client_id is the ``external`` pattern: the credentials file is externally rotated, the SDK re-reads it on every call, no refresh grant is attempted. The spec merged this use case into user_oauth — a client_id is the opt-in signal for refresh.""" _write_profile( tmp_path, "default", config={"type": "authorized_user"}, # migrates to user_oauth, no client_id credentials={ "access_token": "daemon-minted", "expires_at": int(time.time()) + 3600, }, ) tok = CredentialsFile()() assert tok.token == "daemon-minted" @pytest.mark.respx(base_url=BASE_URL) def test_authorized_user_refresh_beta_headers(self, respx_mock: MockRouter, tmp_path: pathlib.Path) -> None: """refresh_token POST must carry oauth-2025-04-20 (unlocks the token endpoint family) but NOT oidc-federation-2026-04-01 (that header is a routing switch that would send this POST to the Go userauth handler, which only accepts jwt-bearer grants).""" _write_profile( tmp_path, "default", config={"type": "authorized_user", "client_id": "cid"}, credentials={ "access_token": "old", "expires_at": int(time.time()) - 1, "refresh_token": "refresh-old", }, ) refresh_route = respx_mock.post(TOKEN_ENDPOINT).mock( return_value=httpx.Response(200, json={"access_token": "new-tok", "expires_in": 3600}) ) CredentialsFile()() req = cast("list[MockRequestCall]", refresh_route.calls)[0].request beta_flags = {f.strip() for f in req.headers["anthropic-beta"].split(",")} assert OAUTH_API_BETA_HEADER in beta_flags assert FEDERATION_BETA_HEADER not in beta_flags assert req.headers["User-Agent"] == f"anthropic-python/{__version__}" def test_user_oauth_with_client_id_missing_refresh_token(self, tmp_path: pathlib.Path) -> None: """A user_oauth profile that has a client_id (refresh mode) but no refresh_token in the credentials file can't actually refresh — raise a clear error rather than 401-looping.""" _write_profile( tmp_path, "default", config={"type": "authorized_user", "client_id": "cid"}, credentials={"access_token": "x", "expires_at": int(time.time()) - 1}, ) with pytest.raises(WorkloadIdentityError, match="'refresh_token'"): CredentialsFile()() # -- common error paths ----------------------------------------------- def test_unknown_type(self, tmp_path: pathlib.Path) -> None: _write_profile(tmp_path, "default", {"type": "mystery"}) with pytest.raises(AnthropicError, match="Unknown authentication.type"): CredentialsFile()() def test_missing_config_file(self, tmp_path: pathlib.Path) -> None: # configs/ dir exists but profile file doesn't (tmp_path / "configs").mkdir() with pytest.raises(AnthropicError, match="Config file not found"): CredentialsFile("nonexistent")() def test_default_credentials_explicit_env_propagates_error( self, tmp_path: pathlib.Path, clean_env: pytest.MonkeyPatch ) -> None: """When ANTHROPIC_PROFILE or ANTHROPIC_CONFIG_DIR is set explicitly, a broken config file surfaces immediately — not swallowed into a 'no auth configured' misdirection.""" clean_env.setenv("ANTHROPIC_CONFIG_DIR", str(tmp_path)) clean_env.setenv("ANTHROPIC_PROFILE", "broken") (tmp_path / "configs").mkdir() (tmp_path / "configs" / "broken.json").write_text("this is not JSON") with pytest.raises(AnthropicError, match="not valid JSON"): default_credentials() # -- security: HTTPS enforcement -------------------------------------- def test_config_base_url_http_rejected(self, tmp_path: pathlib.Path) -> None: """A config file that specifies base_url=http://evil is rejected so a malicious config can't exfiltrate the assertion or refresh token.""" _write_profile( tmp_path, "default", {"type": "external", "base_url": "http://evil.example.com"}, {"access_token": "x"}, ) with pytest.raises(AnthropicError, match="must use https"): CredentialsFile()() def test_config_base_url_localhost_http_allowed(self, tmp_path: pathlib.Path) -> None: """Localhost HTTP is allowed for local oauth_server testing.""" _write_profile( tmp_path, "default", {"type": "external", "base_url": "http://localhost:8080"}, {"access_token": "x", "expires_at": int(time.time()) + 3600}, ) assert CredentialsFile()().token == "x" def test_workload_identity_http_rejected(self) -> None: from anthropic.lib.credentials import WorkloadIdentityCredentials creds = WorkloadIdentityCredentials( identity_token_provider=lambda: "j", federation_rule_id="fdrl_x", organization_id="00000000-0000-0000-0000-000000000000", ) with pytest.raises(AnthropicError, match="must use https"): creds.bind_base_url("http://evil.example.com") # -- security: profile-name validation -------------------------------- def test_profile_name_path_traversal_rejected(self) -> None: with pytest.raises(AnthropicError, match="path separators"): CredentialsFile(profile="evil/shadow")() def test_profile_name_leading_dot_rejected(self) -> None: with pytest.raises(AnthropicError, match="must not start with a dot"): CredentialsFile(profile=".hidden")() def test_profile_name_empty_rejected(self) -> None: with pytest.raises(AnthropicError, match="must not be empty"): CredentialsFile(profile="")() # -- security: credentials file permissions --------------------------- def test_credentials_file_world_readable_rejected(self, tmp_path: pathlib.Path) -> None: if os.name != "posix": pytest.skip("POSIX mode bits only") _write_profile(tmp_path, "default", {"type": "external"}, {"access_token": "x"}) (tmp_path / "credentials" / "default.json").chmod(0o644) with pytest.raises(AnthropicError, match="world-readable"): CredentialsFile()() def test_credentials_file_group_readable_warns( self, tmp_path: pathlib.Path, caplog: pytest.LogCaptureFixture ) -> None: if os.name != "posix": pytest.skip("POSIX mode bits only") _write_profile( tmp_path, "default", {"type": "external"}, {"access_token": "x", "expires_at": int(time.time()) + 3600}, ) (tmp_path / "credentials" / "default.json").chmod(0o640) with caplog.at_level("WARNING", logger="anthropic.lib.credentials._providers"): CredentialsFile()() assert any("group-readable" in rec.message for rec in caplog.records) def test_credentials_file_symlink_rejected(self, tmp_path: pathlib.Path) -> None: if os.name != "posix": pytest.skip("symlink semantics") _write_profile(tmp_path, "default", {"type": "external"}, {"access_token": "x"}) real = tmp_path / "credentials" / "default.json" target = tmp_path / "real-secret.json" target.write_text(real.read_text()) target.chmod(0o600) real.unlink() real.symlink_to(target) with pytest.raises(AnthropicError, match="symlink"): CredentialsFile()() # -- security: redacted error bodies ---------------------------------- def test_workload_identity_error_body_redacted(self) -> None: from anthropic.lib.credentials._workload import _redact_body # Long string truncated. long = "sensitive_" * 100 result = _redact_body(long) assert isinstance(result, str) assert len(result) < len(long) assert "... <" in result # Dict keeps only OAuth standard error fields; the assertion is dropped. dirty = { "error": "invalid_grant", "error_description": "token expired", "assertion": "eyJleHAmple.jwt.sensitive", "refresh_token": "rt_sensitive", } cleaned = _redact_body(dirty) assert cleaned == {"error": "invalid_grant", "error_description": "token expired"} def test_bad_config_json(self, tmp_path: pathlib.Path) -> None: (tmp_path / "configs").mkdir() (tmp_path / "configs" / "default.json").write_text("{not json") with pytest.raises(AnthropicError, match="not valid JSON"): CredentialsFile()() # -- profiles & paths -------------------------------------------------- def test_explicit_profile(self, tmp_path: pathlib.Path) -> None: _write_profile(tmp_path, "work", {"type": "external"}, {"access_token": "work-tok"}) _write_profile(tmp_path, "default", {"type": "external"}, {"access_token": "default-tok"}) assert CredentialsFile("work")().token == "work-tok" assert CredentialsFile()().token == "default-tok" def test_profile_from_env(self, tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch) -> None: _write_profile(tmp_path, "from-env", {"type": "external"}, {"access_token": "env-tok"}) monkeypatch.setenv("ANTHROPIC_PROFILE", "from-env") assert CredentialsFile()().token == "env-tok" def test_profile_from_active_config(self, tmp_path: pathlib.Path) -> None: _write_profile(tmp_path, "pointed-at", {"type": "external"}, {"access_token": "active-tok"}) (tmp_path / "active_config").write_text("pointed-at\n") assert CredentialsFile()().token == "active-tok" def test_env_overrides_active_config(self, tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch) -> None: _write_profile(tmp_path, "from-env", {"type": "external"}, {"access_token": "env-tok"}) _write_profile(tmp_path, "from-file", {"type": "external"}, {"access_token": "file-tok"}) (tmp_path / "active_config").write_text("from-file") monkeypatch.setenv("ANTHROPIC_PROFILE", "from-env") assert CredentialsFile()().token == "env-tok" def test_credentials_path_override(self, tmp_path: pathlib.Path) -> None: """Config's ``credentials_path`` field redirects to a custom location.""" custom = tmp_path / "elsewhere" / "secrets.json" custom.parent.mkdir() custom.write_text(json.dumps({"access_token": "redirected"})) custom.chmod(0o600) _write_profile(tmp_path, "default", {"type": "external", "credentials_path": str(custom)}) assert CredentialsFile()().token == "redirected" @pytest.mark.respx(base_url="https://from-config.example.com") def test_base_url_from_config(self, respx_mock: MockRouter, tmp_path: pathlib.Path) -> None: """Config ``base_url`` is used when no ctor override is given.""" jwt_path = tmp_path / "jwt" jwt_path.write_text("x") _write_profile( tmp_path, "default", { "type": "workload_identity", "base_url": "https://from-config.example.com", "identity_token": {"source": "file", "path": str(jwt_path)}, "federation_rule_id": "f", "organization_id": "o", }, ) token_route = respx_mock.post("/v1/oauth/token").mock( return_value=httpx.Response(200, json={"access_token": "tok", "expires_in": 3600}) ) CredentialsFile()() # no base_url ctor arg → config wins assert str(cast("list[MockRequestCall]", token_route.calls)[0].request.url).startswith( "https://from-config.example.com/" ) # -- review fixes ----------------------------------------------------- def test_chain_and_class_agree_on_profile(self, tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch) -> None: """default_credentials() and CredentialsFile() resolve to the same profile.""" _write_profile(tmp_path, "agreed", {"type": "external"}, {"access_token": "x"}) monkeypatch.setenv("ANTHROPIC_PROFILE", "agreed") result = default_credentials() assert result is not None chain = result.provider direct = CredentialsFile() assert isinstance(chain, CredentialsFile) assert chain.profile == direct.profile == "agreed" assert chain.config_path == direct.config_path # --------------------------------------------------------------------------- # # WorkloadIdentityCredentials # --------------------------------------------------------------------------- # class TestWorkloadIdentityCredentials: @pytest.mark.respx() def test_exchange(self, respx_mock: MockRouter) -> None: respx_mock.post(TOKEN_URL).mock( return_value=httpx.Response( 200, json={"access_token": "sk-ant-oat01-test", "token_type": "Bearer", "expires_in": 600}, ) ) creds = WorkloadIdentityCredentials( identity_token_provider=lambda: "ext.jwt.value", federation_rule_id="fdrl_01abc", organization_id="00000000-0000-0000-0000-000000000000", ) before = time.time() token = creds() after = time.time() assert token.token == "sk-ant-oat01-test" assert token.expires_at is not None assert before + 600 - 2 <= token.expires_at <= after + 600 + 2 calls = cast("list[MockRequestCall]", respx_mock.calls) assert len(calls) == 1 req = calls[0].request # jwt-bearer exchange must carry BOTH: oauth-2025-04-20 unlocks the # token endpoint, oidc-federation-2026-04-01 routes to the federation # handler. beta_flags = {f.strip() for f in req.headers["anthropic-beta"].split(",")} assert OAUTH_API_BETA_HEADER in beta_flags assert FEDERATION_BETA_HEADER in beta_flags assert req.headers["User-Agent"] == f"anthropic-python/{__version__}" body = json.loads(req.content) assert body["grant_type"] == GRANT_TYPE_JWT_BEARER assert body["assertion"] == "ext.jwt.value" assert body["federation_rule_id"] == "fdrl_01abc" assert body["organization_id"] == "00000000-0000-0000-0000-000000000000" assert "service_account_id" not in body assert "workspace_id" not in body assert "scope" not in body @pytest.mark.respx() def test_service_account_included(self, respx_mock: MockRouter) -> None: respx_mock.post(TOKEN_URL).mock(return_value=httpx.Response(200, json={"access_token": "t", "expires_in": 60})) creds = WorkloadIdentityCredentials( identity_token_provider=lambda: "j", federation_rule_id="fdrl_01abc", organization_id="org", service_account_id="svac_01xyz", ) creds() body = json.loads(cast("list[MockRequestCall]", respx_mock.calls)[0].request.content) assert body["service_account_id"] == "svac_01xyz" assert "scope" not in body @pytest.mark.respx() def test_workspace_id_included(self, respx_mock: MockRouter) -> None: respx_mock.post(TOKEN_URL).mock(return_value=httpx.Response(200, json={"access_token": "t", "expires_in": 60})) creds = WorkloadIdentityCredentials( identity_token_provider=lambda: "j", federation_rule_id="fdrl_01abc", organization_id="org", workspace_id="wrkspc_01abc", ) creds() body = json.loads(cast("list[MockRequestCall]", respx_mock.calls)[0].request.content) assert body["workspace_id"] == "wrkspc_01abc" @pytest.mark.respx() def test_workspace_id_default_sentinel(self, respx_mock: MockRouter) -> None: respx_mock.post(TOKEN_URL).mock(return_value=httpx.Response(200, json={"access_token": "t", "expires_in": 60})) creds = WorkloadIdentityCredentials( identity_token_provider=lambda: "j", federation_rule_id="fdrl_01abc", organization_id="org", workspace_id="default", ) creds() body = json.loads(cast("list[MockRequestCall]", respx_mock.calls)[0].request.content) assert body["workspace_id"] == "default" @pytest.mark.respx() def test_scope_is_display_only(self, respx_mock: MockRouter) -> None: """``scope`` is stored on the provider for parity but never sent on the wire — the server derives effective scope from the federation rule.""" respx_mock.post(TOKEN_URL).mock(return_value=httpx.Response(200, json={"access_token": "t", "expires_in": 60})) creds = WorkloadIdentityCredentials( identity_token_provider=lambda: "j", federation_rule_id="fdrl_x", organization_id="org", scope="api:read api:write", ) creds() assert creds.scope == "api:read api:write" body = json.loads(cast("list[MockRequestCall]", respx_mock.calls)[0].request.content) assert "scope" not in body @pytest.mark.respx() def test_exchange_federation_assertion_helper(self, respx_mock: MockRouter) -> None: respx_mock.post(TOKEN_URL).mock( return_value=httpx.Response(200, json={"access_token": "sk-ant-oat01-one", "expires_in": 600}) ) token = exchange_federation_assertion( assertion="ext.jwt.value", federation_rule_id="fdrl_x", organization_id="org_x", workspace_id="wrkspc_x", ) assert token.token == "sk-ant-oat01-one" req = cast("list[MockRequestCall]", respx_mock.calls)[0].request body = json.loads(req.content) assert body["assertion"] == "ext.jwt.value" assert body["federation_rule_id"] == "fdrl_x" assert body["workspace_id"] == "wrkspc_x" @pytest.mark.respx() def test_bind_base_url(self, respx_mock: MockRouter) -> None: """``bind_base_url`` sets the token-exchange URL; unbound → ``DEFAULT_BASE_URL``.""" bound = "https://bound.example" # No bind → DEFAULT_BASE_URL respx_mock.post(TOKEN_URL).mock(return_value=httpx.Response(200, json={"access_token": "t", "expires_in": 60})) WorkloadIdentityCredentials( identity_token_provider=lambda: "j", federation_rule_id="fdrl_x", organization_id="org" )() assert str(cast("list[MockRequestCall]", respx_mock.calls)[-1].request.url) == TOKEN_URL # bound → bound respx_mock.post(f"{bound}{TOKEN_ENDPOINT}").mock( return_value=httpx.Response(200, json={"access_token": "t", "expires_in": 60}) ) creds = WorkloadIdentityCredentials( identity_token_provider=lambda: "j", federation_rule_id="fdrl_x", organization_id="org" ) creds.bind_base_url(bound) creds() assert str(cast("list[MockRequestCall]", respx_mock.calls)[-1].request.url) == f"{bound}{TOKEN_ENDPOINT}" def test_bind_base_url_http_rejected(self) -> None: creds = WorkloadIdentityCredentials( identity_token_provider=lambda: "j", federation_rule_id="fdrl_x", organization_id="org" ) with pytest.raises(AnthropicError, match="must use https"): creds.bind_base_url("http://evil.example") def test_exchange_federation_assertion_http_rejected(self) -> None: with pytest.raises(AnthropicError, match="must use https"): exchange_federation_assertion( assertion="j", federation_rule_id="fdrl_x", organization_id="org_x", base_url="http://example.com", ) @pytest.mark.respx() def test_reinvokes_identity_provider(self, respx_mock: MockRouter) -> None: respx_mock.post(TOKEN_URL).mock(return_value=httpx.Response(200, json={"access_token": "t", "expires_in": 60})) calls: List[int] = [] def jwt_provider() -> str: calls.append(1) return f"jwt-{len(calls)}" creds = WorkloadIdentityCredentials( identity_token_provider=jwt_provider, federation_rule_id="f", organization_id="o", ) creds() creds() assert len(calls) == 2 bodies = [json.loads(c.request.content) for c in cast("list[MockRequestCall]", respx_mock.calls)] assert bodies[0]["assertion"] == "jwt-1" assert bodies[1]["assertion"] == "jwt-2" @pytest.mark.respx() def test_403_raises(self, respx_mock: MockRouter) -> None: respx_mock.post(TOKEN_URL).mock(return_value=httpx.Response(403, json={"error": "assertion rejected"})) creds = WorkloadIdentityCredentials( identity_token_provider=lambda: "j", federation_rule_id="f", organization_id="o", ) with pytest.raises(WorkloadIdentityError) as exc_info: creds() assert exc_info.value.status_code == 403 assert "assertion rejected" in str(exc_info.value) @pytest.mark.respx() def test_503_raises(self, respx_mock: MockRouter) -> None: respx_mock.post(TOKEN_URL).mock(return_value=httpx.Response(503, text="overloaded")) creds = WorkloadIdentityCredentials( identity_token_provider=lambda: "j", federation_rule_id="f", organization_id="o", ) with pytest.raises(WorkloadIdentityError) as exc_info: creds() assert exc_info.value.status_code == 503 @pytest.mark.respx() def test_request_id_surfaced_on_error(self, respx_mock: MockRouter) -> None: respx_mock.post(TOKEN_URL).mock( return_value=httpx.Response(400, json={"error": "invalid_grant"}, headers={"Request-Id": "req_abc123"}) ) creds = WorkloadIdentityCredentials( identity_token_provider=lambda: "j", federation_rule_id="f", organization_id="o", ) with pytest.raises(WorkloadIdentityError) as exc_info: creds() assert exc_info.value.request_id == "req_abc123" assert "[request_id=req_abc123]" in str(exc_info.value) @pytest.mark.respx() def test_401_without_workspace_id_includes_hint(self, respx_mock: MockRouter) -> None: """A failed exchange with no workspace_id should surface all three hint parts: the federation-rule lead-in, the multi-workspace fix, and the Console auth-events pointer.""" respx_mock.post(TOKEN_URL).mock(return_value=httpx.Response(401, json={"error": "unauthorized"})) creds = WorkloadIdentityCredentials( identity_token_provider=lambda: "j", federation_rule_id="f", organization_id="o", ) with pytest.raises(WorkloadIdentityError) as exc_info: creds() message = str(exc_info.value) assert "Ensure your federation rule matches your identity token" in message assert "ANTHROPIC_WORKSPACE_ID" in message assert "scoped to multiple workspaces" in message assert "workspace_id" in message assert "View your authentication events" in message @pytest.mark.respx() def test_401_with_workspace_id_set_omits_workspace_hint(self, respx_mock: MockRouter) -> None: """When workspace_id is already set the multi-workspace fix is noise, but the federation-rule lead-in and Console pointer still apply.""" respx_mock.post(TOKEN_URL).mock(return_value=httpx.Response(401, json={"error": "unauthorized"})) creds = WorkloadIdentityCredentials( identity_token_provider=lambda: "j", federation_rule_id="f", organization_id="o", workspace_id="wrkspc_x", ) with pytest.raises(WorkloadIdentityError) as exc_info: creds() message = str(exc_info.value) assert "Ensure your federation rule matches your identity token" in message assert "View your authentication events" in message assert "ANTHROPIC_WORKSPACE_ID" not in message assert "scoped to multiple workspaces" not in message @pytest.mark.respx() def test_non_401_omits_hint(self, respx_mock: MockRouter) -> None: """The hint is 401-specific; a 5xx or 400 shouldn't suggest a config change.""" respx_mock.post(TOKEN_URL).mock(return_value=httpx.Response(500, json={"error": "server_error"})) creds = WorkloadIdentityCredentials( identity_token_provider=lambda: "j", federation_rule_id="f", organization_id="o", ) with pytest.raises(WorkloadIdentityError) as exc_info: creds() message = str(exc_info.value) assert "Ensure your federation rule" not in message assert "ANTHROPIC_WORKSPACE_ID" not in message assert "View your authentication events" not in message def test_oversized_assertion_rejected(self) -> None: big_jwt = "x" * (16 * 1024 + 1) creds = WorkloadIdentityCredentials( identity_token_provider=lambda: big_jwt, federation_rule_id="f", organization_id="o", ) with pytest.raises(WorkloadIdentityError, match="exceeds the 16384-byte limit"): creds() @pytest.mark.respx() def test_non_bearer_token_type_rejected(self, respx_mock: MockRouter) -> None: respx_mock.post(TOKEN_URL).mock( return_value=httpx.Response(200, json={"access_token": "t", "expires_in": 60, "token_type": "MAC"}) ) creds = WorkloadIdentityCredentials( identity_token_provider=lambda: "j", federation_rule_id="f", organization_id="o", ) with pytest.raises(WorkloadIdentityError, match="unsupported token_type 'MAC'"): creds() @pytest.mark.respx() def test_bearer_token_type_case_insensitive(self, respx_mock: MockRouter) -> None: respx_mock.post(TOKEN_URL).mock( return_value=httpx.Response(200, json={"access_token": "t", "expires_in": 60, "token_type": "bearer"}) ) creds = WorkloadIdentityCredentials( identity_token_provider=lambda: "j", federation_rule_id="f", organization_id="o", ) assert creds().token == "t" @pytest.mark.respx() def test_oversized_response_body_rejected(self, respx_mock: MockRouter) -> None: respx_mock.post(TOKEN_URL).mock(return_value=httpx.Response(200, content=b"x" * ((1 << 20) + 1))) creds = WorkloadIdentityCredentials( identity_token_provider=lambda: "j", federation_rule_id="f", organization_id="o", ) with pytest.raises(WorkloadIdentityError, match="response body exceeds"): creds() # --------------------------------------------------------------------------- # # Profile env-var fill (PY-01) + identity_token validation (PY-06) # --------------------------------------------------------------------------- # class TestProfileEnvFill: """PY-01: profile fields left empty are filled from ANTHROPIC_* env vars, matching Go's ``fillMissingFromEnv`` precedence (file wins, env fills gaps).""" @pytest.fixture(autouse=True) def _isolate(self, monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path) -> None: for var in _ALL_ENV: monkeypatch.delenv(var, raising=False) monkeypatch.setenv("ANTHROPIC_CONFIG_DIR", str(tmp_path)) def test_env_fills_missing_organization_id(self, tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_ORGANIZATION_ID", "org_from_env") monkeypatch.setenv("ANTHROPIC_IDENTITY_TOKEN_FILE", str(tmp_path / "tok")) (tmp_path / "tok").write_text("jwt") # Profile omits organization_id; env supplies it. _write_profile( tmp_path, "default", config={ "authentication": { "type": "oidc_federation", "federation_rule_id": "fdrl_01abc", } }, ) provider = CredentialsFile() # Trigger _load_config via the workload-build path. delegate = provider._build_workload_delegate(provider._auth_block()) # pyright: ignore[reportPrivateUsage] assert delegate._organization_id == "org_from_env" # pyright: ignore[reportPrivateUsage] def test_profile_value_wins_over_env(self, tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_ORGANIZATION_ID", "org_from_env") monkeypatch.setenv("ANTHROPIC_WORKSPACE_ID", "wrkspc_env") monkeypatch.setenv("ANTHROPIC_IDENTITY_TOKEN_FILE", str(tmp_path / "tok")) (tmp_path / "tok").write_text("jwt") _write_profile( tmp_path, "default", config={ "organization_id": "org_from_file", "workspace_id": "wrkspc_file", "authentication": { "type": "oidc_federation", "federation_rule_id": "fdrl_01abc", }, }, ) provider = CredentialsFile() delegate = provider._build_workload_delegate(provider._auth_block()) # pyright: ignore[reportPrivateUsage] assert delegate._organization_id == "org_from_file" # pyright: ignore[reportPrivateUsage] assert delegate._workspace_id == "wrkspc_file" # pyright: ignore[reportPrivateUsage] def test_env_fills_missing_workspace_id(self, tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_ORGANIZATION_ID", "org_x") monkeypatch.setenv("ANTHROPIC_WORKSPACE_ID", "wrkspc_from_env") monkeypatch.setenv("ANTHROPIC_IDENTITY_TOKEN_FILE", str(tmp_path / "tok")) (tmp_path / "tok").write_text("jwt") _write_profile( tmp_path, "default", config={ "authentication": { "type": "oidc_federation", "federation_rule_id": "fdrl_01abc", } }, ) provider = CredentialsFile() delegate = provider._build_workload_delegate(provider._auth_block()) # pyright: ignore[reportPrivateUsage] assert delegate._workspace_id == "wrkspc_from_env" # pyright: ignore[reportPrivateUsage] def test_env_workspace_id_fills_user_oauth(self, tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch) -> None: """``ANTHROPIC_WORKSPACE_ID`` fills ``workspace_id`` uniformly across profile types — not just federation. This pins the precedence model: ctor override > env var > profile, regardless of ``auth.type``. For ``user_oauth`` the filled value surfaces as the ``anthropic-workspace-id`` request header (federation routes it into the exchange body instead).""" monkeypatch.setenv("ANTHROPIC_WORKSPACE_ID", "wrkspc_env") creds_path = tmp_path / "creds.json" creds_path.write_text(json.dumps({"type": "oauth_token", "access_token": "tok", "expires_at": None})) _write_profile( tmp_path, "default", config={ "authentication": { "type": "user_oauth", "client_id": "cid", "credentials_path": str(creds_path), } }, ) provider = CredentialsFile() assert provider.extra_headers() == {"anthropic-workspace-id": "wrkspc_env"} class TestIdentityTokenValidation: """PY-06: identity_token.source 'file' with empty path is a config bug, not an env-var fallback signal.""" @pytest.fixture(autouse=True) def _isolate(self, monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path) -> None: for var in _ALL_ENV: monkeypatch.delenv(var, raising=False) monkeypatch.setenv("ANTHROPIC_CONFIG_DIR", str(tmp_path)) def test_empty_path_raises(self, tmp_path: pathlib.Path) -> None: _write_profile( tmp_path, "default", config={ "organization_id": "org", "authentication": { "type": "oidc_federation", "federation_rule_id": "fdrl_01abc", "identity_token": {"source": "file", "path": ""}, }, }, ) provider = CredentialsFile() with pytest.raises(AnthropicError, match="non-empty path"): provider._build_workload_delegate(provider._auth_block()) # pyright: ignore[reportPrivateUsage] # --------------------------------------------------------------------------- # # TokenCache # --------------------------------------------------------------------------- # class FakeClock: def __init__(self, now: float = 1000.0) -> None: self.now = now def __call__(self) -> float: return self.now class CountingProvider: def __init__(self, tokens: List[AccessToken]) -> None: self.tokens = tokens self.calls = 0 def __call__(self, *, force_refresh: bool = False) -> AccessToken: # noqa: ARG002 self.calls += 1 return self.tokens[min(self.calls - 1, len(self.tokens) - 1)] class TestTokenCache: def test_first_call_fetches(self) -> None: provider = CountingProvider([AccessToken("a", expires_at=2000)]) cache = TokenCache(provider, time_source=FakeClock(1000)) assert cache.get_token() == "a" assert provider.calls == 1 def test_no_expiry_never_refreshes(self) -> None: provider = CountingProvider([AccessToken("a", expires_at=None)]) clock = FakeClock(1000) cache = TokenCache(provider, time_source=clock) cache.get_token() clock.now = 999999 cache.get_token() cache.get_token() assert provider.calls == 1 def test_fresh_token_not_refetched(self) -> None: clock = FakeClock(1000) provider = CountingProvider([AccessToken("a", expires_at=1000 + 600)]) cache = TokenCache(provider, time_source=clock) cache.get_token() cache.get_token() cache.get_token() assert provider.calls == 1 def test_advisory_refresh_success(self) -> None: clock = FakeClock(1000) provider = CountingProvider([AccessToken("a", expires_at=1000 + 600), AccessToken("b", expires_at=1000 + 1200)]) cache = TokenCache(provider, time_source=clock) assert cache.get_token() == "a" clock.now = 1000 + 600 - 60 # 60s remaining: advisory window (30 < 60 < 120) assert cache.get_token() == "b" assert provider.calls == 2 def test_advisory_refresh_failure_serves_stale(self, caplog: pytest.LogCaptureFixture) -> None: clock = FakeClock(1000) first = AccessToken("a", expires_at=1000 + 600) class P: calls = 0 def __call__(self, *, force_refresh: bool = False) -> AccessToken: # noqa: ARG002 self.calls += 1 if self.calls == 1: return first raise WorkloadIdentityError("backend down") provider = P() cache = TokenCache(provider, time_source=clock) assert cache.get_token() == "a" clock.now = 1000 + 600 - 60 # advisory window with caplog.at_level(logging.WARNING): assert cache.get_token() == "a" # stale served assert any("Advisory token refresh failed" in r.message for r in caplog.records) assert provider.calls == 2 def test_mandatory_refresh_failure_raises(self) -> None: clock = FakeClock(1000) first = AccessToken("a", expires_at=1000 + 600) class P: calls = 0 def __call__(self, *, force_refresh: bool = False) -> AccessToken: # noqa: ARG002 self.calls += 1 if self.calls == 1: return first raise WorkloadIdentityError("backend down") provider = P() cache = TokenCache(provider, time_source=clock) cache.get_token() clock.now = 1000 + 600 - 10 # 10s remaining: mandatory window with pytest.raises(WorkloadIdentityError, match="backend down"): cache.get_token() def test_expired_is_mandatory(self) -> None: clock = FakeClock(1000) provider = CountingProvider([AccessToken("a", expires_at=1000 + 600), AccessToken("b", expires_at=1000 + 1200)]) cache = TokenCache(provider, time_source=clock) cache.get_token() clock.now = 1000 + 700 # already expired assert cache.get_token() == "b" assert provider.calls == 2 def test_invalidate(self) -> None: provider = CountingProvider([AccessToken("a", None), AccessToken("b", None)]) cache = TokenCache(provider) assert cache.get_token() == "a" assert cache.get_token() == "a" assert provider.calls == 1 cache.invalidate() assert cache.get_token() == "b" assert provider.calls == 2 def test_retries_once_on_401_from_token_endpoint(self) -> None: """If the provider raises a 401 WorkloadIdentityError, the cache retries once.""" class Provider401ThenSuccess: calls = 0 def __call__(self, *, force_refresh: bool = False) -> AccessToken: # noqa: ARG002 self.calls += 1 if self.calls == 1: raise WorkloadIdentityError("token exchange failed", status_code=401, body="unauthorized") return AccessToken("fresh", expires_at=None) provider = Provider401ThenSuccess() cache = TokenCache(provider) assert cache.get_token() == "fresh" assert provider.calls == 2 def test_no_retry_on_non_401_error(self) -> None: """Non-401 errors from the provider are not retried.""" class Provider400: calls = 0 def __call__(self, *, force_refresh: bool = False) -> AccessToken: # noqa: ARG002 self.calls += 1 raise WorkloadIdentityError("bad request", status_code=400, body="invalid") provider = Provider400() cache = TokenCache(provider) with pytest.raises(WorkloadIdentityError, match="bad request"): cache.get_token() assert provider.calls == 1 def test_retry_on_401_still_fails_raises(self) -> None: """If both attempts return 401, the second error propagates.""" class AlwaysFails401: calls = 0 def __call__(self, *, force_refresh: bool = False) -> AccessToken: # noqa: ARG002 self.calls += 1 raise WorkloadIdentityError("still unauthorized", status_code=401, body="unauthorized") provider = AlwaysFails401() cache = TokenCache(provider) with pytest.raises(WorkloadIdentityError, match="still unauthorized"): cache.get_token() assert provider.calls == 2 def test_concurrent_mandatory_refresh_single_flight(self) -> None: """N concurrent callers in the mandatory (expired) window trigger exactly one provider call — waiters block on the leader's event.""" import threading as _threading provider_calls: List[int] = [] barrier = _threading.Barrier(8) def slow_provider(*, force_refresh: bool = False) -> AccessToken: # noqa: ARG001 provider_calls.append(1) # Hold the refresh long enough for the other threads to queue up. time.sleep(0.1) return AccessToken(token="fresh", expires_at=None) cache = TokenCache(slow_provider) results: List[str] = [] lock = _threading.Lock() def worker() -> None: barrier.wait() tok = cache.get_token() with lock: results.append(tok) threads = [_threading.Thread(target=worker) for _ in range(8)] for t in threads: t.start() for t in threads: t.join() assert len(provider_calls) == 1 assert results == ["fresh"] * 8 def test_advisory_caller_skips_when_refresh_in_flight(self) -> None: """A caller in the advisory window does NOT start a second refresh and does NOT wait on a running one — it just returns the cached token.""" import threading as _threading clock = FakeClock(1000) tokens = [AccessToken("a", expires_at=1000 + 600), AccessToken("b", expires_at=1000 + 1800)] provider_calls: List[int] = [] refresh_started = _threading.Event() release_refresh = _threading.Event() def provider(*, force_refresh: bool = False) -> AccessToken: # noqa: ARG001 provider_calls.append(1) refresh_started.set() release_refresh.wait(timeout=2) return tokens[len(provider_calls) - 1] cache = TokenCache(provider, time_source=clock) # Prime the cache with token 'a'. assert cache.get_token() == "a" # Move into the advisory window and kick off a refresh on a background # thread. It will block in the provider until we release it. clock.now = 1000 + 600 - 60 leader = _threading.Thread(target=cache.get_token) leader.start() assert refresh_started.wait(timeout=2) # A second caller in the advisory window should see the in-flight # refresh and return the cached token immediately — without waiting. t0 = time.monotonic() observed = cache.get_token() elapsed = time.monotonic() - t0 assert observed == "a" assert elapsed < 0.1, f"advisory caller waited for leader ({elapsed:.3f}s)" release_refresh.set() leader.join() # Two provider calls total: the priming call + the leader's advisory # refresh. The bystander did NOT call the provider. assert len(provider_calls) == 2 def test_invalidate_forces_provider_refresh(self) -> None: """PY-02: after invalidate(), the next provider call receives force_refresh=True so providers with on-disk caches bypass their freshness short-circuit. Without this, a 401 retry would re-read the same expired token from disk and serve it again.""" force_seen: List[bool] = [] tokens = iter([AccessToken("a", expires_at=None), AccessToken("b", expires_at=None)]) def provider(*, force_refresh: bool = False) -> AccessToken: # noqa: ARG001 force_seen.append(force_refresh) return next(tokens) cache = TokenCache(provider) assert cache.get_token() == "a" cache.invalidate() assert cache.get_token() == "b" assert force_seen == [False, True], "force_refresh must be True on the post-invalidate call" def test_zero_arg_provider_backward_compat(self) -> None: """Providers from before the force_refresh kwarg was added (the old ``Callable[[], AccessToken]`` shape) must still work — the kwarg- binding TypeError is caught and the provider is re-invoked positionally.""" calls: List[int] = [] def legacy_provider() -> AccessToken: calls.append(1) return AccessToken("legacy", expires_at=None) cache = TokenCache(legacy_provider) # type: ignore[arg-type] assert cache.get_token() == "legacy" # invalidate() sets _next_force; the zero-arg fallback must still fire. cache.invalidate() assert cache.get_token() == "legacy" assert len(calls) == 2 def test_next_force_preserved_on_provider_failure(self) -> None: """If invalidate() set the force flag and the provider then raises, the flag must NOT be consumed — a subsequent retry must still see force_refresh=True. Otherwise the retry serves the stale disk token PY-02 was added to bypass.""" force_seen: List[bool] = [] attempts = iter([RuntimeError("transient"), AccessToken("ok", expires_at=None)]) def provider(*, force_refresh: bool = False) -> AccessToken: # noqa: ARG001 force_seen.append(force_refresh) v = next(attempts) if isinstance(v, BaseException): raise v return v cache = TokenCache(provider) cache.invalidate() with pytest.raises(RuntimeError): cache.get_token() # Retry: force flag must still be set. assert cache.get_token() == "ok" assert force_seen == [True, True], "force flag must survive provider failure" def test_advisory_refresh_backoff_after_failure(self) -> None: """PY-07: after an advisory refresh failure, subsequent advisory callers within ADVISORY_REFRESH_BACKOFF_SECONDS reuse the cached token instead of re-attempting the provider — preventing an outage during the advisory window from being hammered at request rate.""" from anthropic.lib.credentials._workload import WorkloadIdentityError as _WIE clock = FakeClock(1000) provider_calls: List[int] = [] def provider(*, force_refresh: bool = False) -> AccessToken: # noqa: ARG001 provider_calls.append(1) if len(provider_calls) == 1: return AccessToken("a", expires_at=1000 + 600) raise _WIE("token endpoint down", status_code=503) cache = TokenCache(provider, time_source=clock) assert cache.get_token() == "a" # Step into the advisory window. clock.now = 1000 + 600 - 60 # First advisory call: fires the provider, fails, serves cached. assert cache.get_token() == "a" assert len(provider_calls) == 2 # Second advisory call within backoff window: must reuse cached # WITHOUT calling the provider. clock.now += 3 assert cache.get_token() == "a" assert len(provider_calls) == 2, "provider must not be retried within backoff window" # After the backoff window, the next advisory call retries. clock.now += 3 # total +6 since failure, > 5s backoff assert cache.get_token() == "a" # still serves cached after retry fails assert len(provider_calls) == 3, "provider must be retried after backoff window" # --------------------------------------------------------------------------- # # default_credentials chain # --------------------------------------------------------------------------- # @pytest.mark.usefixtures("no_default_creds_file") class TestDefaultCredentials: def test_api_key_returns_none(self, clean_env: pytest.MonkeyPatch) -> None: clean_env.setenv("ANTHROPIC_API_KEY", "sk-ant-api-key") assert default_credentials() is None def test_auth_token_returns_static(self, clean_env: pytest.MonkeyPatch) -> None: clean_env.setenv("ANTHROPIC_AUTH_TOKEN", "bearer-tok") result = default_credentials() assert result is not None assert isinstance(result.provider, StaticToken) assert result.provider().token == "bearer-tok" def test_config_dir_env_triggers_tier1(self, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path) -> None: """ANTHROPIC_CONFIG_DIR set → tier 1 fires even if dir is empty (explicit opt-in).""" # Re-point _config_dir at tmp_path (the no_default_creds_file fixture # already patched it to an empty dir; override that here). clean_env.setattr("anthropic.lib.credentials._constants._config_dir", lambda: tmp_path) clean_env.setenv("ANTHROPIC_CONFIG_DIR", str(tmp_path)) _write_profile(tmp_path, "default", {"type": "external"}, {"access_token": "from-file"}) result = default_credentials() assert result is not None assert isinstance(result.provider, CredentialsFile) assert result.provider().token == "from-file" def test_profile_env_triggers_tier1(self, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path) -> None: """ANTHROPIC_PROFILE set → tier 1 fires.""" clean_env.setattr("anthropic.lib.credentials._constants._config_dir", lambda: tmp_path) clean_env.setenv("ANTHROPIC_PROFILE", "work") _write_profile(tmp_path, "work", {"type": "external"}, {"access_token": "from-work"}) result = default_credentials() assert result is not None assert isinstance(result.provider, CredentialsFile) assert result.provider().token == "from-work" def test_default_dir_with_configs_triggers_tier1( self, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path ) -> None: """No env vars but configs/ has files → tier 1 fires via _has_any_config().""" clean_env.setattr("anthropic.lib.credentials._constants._config_dir", lambda: tmp_path) _write_profile(tmp_path, "default", {"type": "external"}, {"access_token": "from-default"}) result = default_credentials() assert result is not None assert isinstance(result.provider, CredentialsFile) def test_workload_identity_tier(self, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path) -> None: f = tmp_path / "jwt" f.write_text("ext-jwt") clean_env.setenv("ANTHROPIC_IDENTITY_TOKEN_FILE", str(f)) clean_env.setenv("ANTHROPIC_FEDERATION_RULE_ID", "fdrl_01abc") clean_env.setenv("ANTHROPIC_ORGANIZATION_ID", "org-uuid") result = default_credentials() assert result is not None assert isinstance(result.provider, WorkloadIdentityCredentials) def test_workload_identity_literal_token(self, clean_env: pytest.MonkeyPatch) -> None: clean_env.setenv("ANTHROPIC_IDENTITY_TOKEN", "literal-jwt") clean_env.setenv("ANTHROPIC_FEDERATION_RULE_ID", "fdrl_01abc") clean_env.setenv("ANTHROPIC_ORGANIZATION_ID", "org-uuid") result = default_credentials() assert result is not None assert isinstance(result.provider, WorkloadIdentityCredentials) def test_workload_identity_scope_env(self, clean_env: pytest.MonkeyPatch) -> None: clean_env.setenv("ANTHROPIC_IDENTITY_TOKEN", "literal-jwt") clean_env.setenv("ANTHROPIC_FEDERATION_RULE_ID", "fdrl_01abc") clean_env.setenv("ANTHROPIC_ORGANIZATION_ID", "org-uuid") clean_env.setenv("ANTHROPIC_SCOPE", "api:read api:write") result = default_credentials() assert result is not None assert isinstance(result.provider, WorkloadIdentityCredentials) assert result.provider.scope == "api:read api:write" def test_workload_identity_workspace_id_env(self, clean_env: pytest.MonkeyPatch) -> None: clean_env.setenv("ANTHROPIC_IDENTITY_TOKEN", "literal-jwt") clean_env.setenv("ANTHROPIC_FEDERATION_RULE_ID", "fdrl_01abc") clean_env.setenv("ANTHROPIC_ORGANIZATION_ID", "org-uuid") clean_env.setenv("ANTHROPIC_WORKSPACE_ID", "wrkspc_01abc") result = default_credentials() assert result is not None provider = result.provider assert isinstance(provider, WorkloadIdentityCredentials) assert provider._workspace_id == "wrkspc_01abc" # pyright: ignore[reportPrivateUsage] @pytest.mark.respx() def test_workload_identity_workspace_id_env_empty_treated_unset( self, clean_env: pytest.MonkeyPatch, respx_mock: MockRouter ) -> None: """``ANTHROPIC_WORKSPACE_ID=""`` (a defaulted-but-empty CI variable) is treated as unset — never put ``"workspace_id": ""`` on the wire.""" clean_env.setenv("ANTHROPIC_IDENTITY_TOKEN", "literal-jwt") clean_env.setenv("ANTHROPIC_FEDERATION_RULE_ID", "fdrl_01abc") clean_env.setenv("ANTHROPIC_ORGANIZATION_ID", "org-uuid") clean_env.setenv("ANTHROPIC_WORKSPACE_ID", "") respx_mock.post(TOKEN_URL).mock(return_value=httpx.Response(200, json={"access_token": "t", "expires_in": 60})) result = default_credentials() assert result is not None provider = result.provider assert isinstance(provider, WorkloadIdentityCredentials) assert provider._workspace_id is None # pyright: ignore[reportPrivateUsage] provider() body = json.loads(cast("list[MockRequestCall]", respx_mock.calls)[0].request.content) assert "workspace_id" not in body def test_workload_identity_literal_token_reads_fresh(self, clean_env: pytest.MonkeyPatch) -> None: """Fix 4: ANTHROPIC_IDENTITY_TOKEN must be re-read on every provider invocation, not captured into a closure at chain-construction time.""" clean_env.setenv("ANTHROPIC_IDENTITY_TOKEN", "jwt-v1") clean_env.setenv("ANTHROPIC_FEDERATION_RULE_ID", "fdrl_01abc") clean_env.setenv("ANTHROPIC_ORGANIZATION_ID", "org-uuid") result = default_credentials() assert result is not None provider = result.provider assert isinstance(provider, WorkloadIdentityCredentials) assert provider._identity_token_provider() == "jwt-v1" # pyright: ignore[reportPrivateUsage] clean_env.setenv("ANTHROPIC_IDENTITY_TOKEN", "jwt-v2") assert provider._identity_token_provider() == "jwt-v2" # pyright: ignore[reportPrivateUsage] def test_workload_identity_requires_all_three(self, clean_env: pytest.MonkeyPatch) -> None: # only two of three set clean_env.setenv("ANTHROPIC_IDENTITY_TOKEN", "literal-jwt") clean_env.setenv("ANTHROPIC_FEDERATION_RULE_ID", "fdrl_01abc") assert default_credentials() is None def test_env_federation_beats_fallback_on_disk_profile( self, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path ) -> None: """Step 4 (env federation trio) sits above step 5 (fallback on-disk profile) in the precedence spec: a machine with WIF env vars wired up must use WIF even if a leftover ``default`` profile exists on disk. A user who wants the on-disk profile must set ``ANTHROPIC_PROFILE`` explicitly (step 3), which would win. """ clean_env.setattr("anthropic.lib.credentials._constants._config_dir", lambda: tmp_path) _write_profile(tmp_path, "default", {"type": "external"}, {"access_token": "from-on-disk-profile"}) clean_env.setenv("ANTHROPIC_IDENTITY_TOKEN", "literal-jwt") clean_env.setenv("ANTHROPIC_FEDERATION_RULE_ID", "fdrl_01abc") clean_env.setenv("ANTHROPIC_ORGANIZATION_ID", "00000000-0000-0000-0000-000000000000") result = default_credentials() assert result is not None assert isinstance(result.provider, WorkloadIdentityCredentials) def test_explicit_profile_beats_env_federation(self, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path) -> None: """Step 3 (ANTHROPIC_PROFILE) sits above step 4 (env federation). An explicit profile selection wins over a federation-configured environment. """ clean_env.setattr("anthropic.lib.credentials._constants._config_dir", lambda: tmp_path) _write_profile(tmp_path, "dev", {"type": "external"}, {"access_token": "from-dev-profile"}) clean_env.setenv("ANTHROPIC_PROFILE", "dev") clean_env.setenv("ANTHROPIC_IDENTITY_TOKEN", "literal-jwt") clean_env.setenv("ANTHROPIC_FEDERATION_RULE_ID", "fdrl_01abc") clean_env.setenv("ANTHROPIC_ORGANIZATION_ID", "00000000-0000-0000-0000-000000000000") result = default_credentials() assert result is not None assert isinstance(result.provider, CredentialsFile) assert result.provider().token == "from-dev-profile" @pytest.mark.usefixtures("clean_env") def test_nothing_set_returns_none(self) -> None: assert default_credentials() is None # --------------------------------------------------------------------------- # # Anthropic(credentials=...) integration # --------------------------------------------------------------------------- # def _mock_token_endpoint(respx_mock: MockRouter) -> None: respx_mock.post(TOKEN_URL).mock( return_value=httpx.Response( 200, json={"access_token": "sk-ant-oat01-test", "token_type": "Bearer", "expires_in": 600}, ) ) def _mock_messages_endpoint(respx_mock: MockRouter) -> None: respx_mock.post(f"{BASE_URL}/v1/messages").mock( return_value=httpx.Response( 200, json={ "id": "msg_01", "type": "message", "role": "assistant", "model": "claude-opus-4-5", "content": [{"type": "text", "text": "hi"}], "stop_reason": "end_turn", "stop_sequence": None, "usage": {"input_tokens": 1, "output_tokens": 1}, }, ) ) def _send_message(client: Anthropic) -> None: client.messages.create( max_tokens=1, model="claude-opus-4-5", messages=[{"role": "user", "content": "hi"}], ) @pytest.mark.usefixtures("clean_env", "no_default_creds_file") class TestAnthropicCredentials: @pytest.mark.respx() def test_messages_request_has_bearer_and_beta(self, respx_mock: MockRouter) -> None: _mock_token_endpoint(respx_mock) _mock_messages_endpoint(respx_mock) client = Anthropic( credentials=WorkloadIdentityCredentials( identity_token_provider=lambda: "ext-jwt", federation_rule_id="fdrl_01abc", organization_id="org-uuid", ), ) _send_message(client) calls = cast("list[MockRequestCall]", respx_mock.calls) msg_calls = [c for c in calls if str(c.request.url).endswith("/v1/messages")] assert len(msg_calls) == 1 req = msg_calls[0].request assert req.headers["Authorization"] == "Bearer sk-ant-oat01-test" # Authenticated API requests carry oauth-2025-04-20 (API beta) — # NOT the federation routing switch, which is only for jwt-bearer # exchanges at /v1/oauth/token. msg_flags = {f.strip() for f in req.headers["anthropic-beta"].split(",")} assert OAUTH_API_BETA_HEADER in msg_flags assert FEDERATION_BETA_HEADER not in msg_flags assert "X-Api-Key" not in req.headers @pytest.mark.respx() def test_workload_identity_inherits_client_base_url(self, respx_mock: MockRouter) -> None: """An explicitly-passed WorkloadIdentityCredentials with no ``base_url`` adopts the client's ``base_url`` for the token exchange, so the user doesn't have to pass the same URL twice.""" custom_base = "https://api-staging.example" respx_mock.post(f"{custom_base}{TOKEN_ENDPOINT}").mock( return_value=httpx.Response(200, json={"access_token": "tok-staging", "expires_in": 600}) ) respx_mock.post(f"{custom_base}/v1/messages").mock( return_value=httpx.Response( 200, json={ "id": "msg_01", "type": "message", "role": "assistant", "model": "claude-opus-4-5", "content": [{"type": "text", "text": "hi"}], "stop_reason": "end_turn", "stop_sequence": None, "usage": {"input_tokens": 1, "output_tokens": 1}, }, ) ) client = Anthropic( credentials=WorkloadIdentityCredentials( identity_token_provider=lambda: "ext-jwt", federation_rule_id="fdrl_01abc", organization_id="org-uuid", ), base_url=custom_base, ) _send_message(client) calls = cast("list[MockRequestCall]", respx_mock.calls) token_calls = [c for c in calls if TOKEN_ENDPOINT in str(c.request.url)] assert len(token_calls) == 1 assert str(token_calls[0].request.url) == f"{custom_base}{TOKEN_ENDPOINT}" msg_calls = [c for c in calls if str(c.request.url).endswith("/v1/messages")] assert msg_calls[0].request.headers["Authorization"] == "Bearer tok-staging" @pytest.mark.respx() def test_token_endpoint_called_once_across_requests(self, respx_mock: MockRouter) -> None: _mock_token_endpoint(respx_mock) _mock_messages_endpoint(respx_mock) client = Anthropic( credentials=WorkloadIdentityCredentials( identity_token_provider=lambda: "ext-jwt", federation_rule_id="fdrl_01abc", organization_id="org-uuid", ), ) _send_message(client) _send_message(client) calls = cast("list[MockRequestCall]", respx_mock.calls) token_calls = [c for c in calls if TOKEN_ENDPOINT in str(c.request.url)] msg_calls = [c for c in calls if str(c.request.url).endswith("/v1/messages")] assert len(token_calls) == 1 assert len(msg_calls) == 2 @pytest.mark.respx() def test_static_token_credentials(self, respx_mock: MockRouter) -> None: _mock_messages_endpoint(respx_mock) client = Anthropic(credentials=StaticToken("static-bearer")) _send_message(client) req = cast("list[MockRequestCall]", respx_mock.calls)[0].request assert req.headers["Authorization"] == "Bearer static-bearer" assert OAUTH_API_BETA_HEADER in req.headers["anthropic-beta"] @pytest.mark.respx() def test_beta_header_not_duplicated(self, respx_mock: MockRouter) -> None: _mock_messages_endpoint(respx_mock) client = Anthropic(credentials=StaticToken("static-bearer")) client.messages.create( max_tokens=1, model="claude-opus-4-5", messages=[{"role": "user", "content": "hi"}], extra_headers={"anthropic-beta": OAUTH_API_BETA_HEADER}, ) req = cast("list[MockRequestCall]", respx_mock.calls)[0].request assert req.headers["anthropic-beta"] == OAUTH_API_BETA_HEADER @pytest.mark.respx() def test_beta_header_dedupe_is_token_based(self, respx_mock: MockRouter) -> None: """The dedupe check matches whole comma-separated flags, not substrings. A pre-existing flag containing the OAuth beta as a prefix (e.g. a future suffixed variant) must NOT prevent the SDK from adding its own flag.""" _mock_token_endpoint(respx_mock) _mock_messages_endpoint(respx_mock) client = Anthropic( credentials=WorkloadIdentityCredentials( identity_token_provider=lambda: "ext-jwt", federation_rule_id="fdrl_x", organization_id="org-uuid", ), ) client.messages.create( max_tokens=1, model="claude-opus-4-5", messages=[{"role": "user", "content": "hi"}], extra_headers={"anthropic-beta": f"{OAUTH_API_BETA_HEADER}-future-variant"}, ) msg_calls = [ c for c in cast("list[MockRequestCall]", respx_mock.calls) if str(c.request.url).endswith("/v1/messages") ] flags = [f.strip() for f in msg_calls[0].request.headers["anthropic-beta"].split(",")] assert OAUTH_API_BETA_HEADER in flags assert f"{OAUTH_API_BETA_HEADER}-future-variant" in flags @pytest.mark.respx() def test_zero_config_workload_identity_from_env( self, respx_mock: MockRouter, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path ) -> None: f = tmp_path / "jwt" f.write_text("env-jwt") clean_env.setenv("ANTHROPIC_IDENTITY_TOKEN_FILE", str(f)) clean_env.setenv("ANTHROPIC_FEDERATION_RULE_ID", "fdrl_01abc") clean_env.setenv("ANTHROPIC_ORGANIZATION_ID", "org-uuid") _mock_token_endpoint(respx_mock) _mock_messages_endpoint(respx_mock) client = Anthropic() assert isinstance(client.credentials, WorkloadIdentityCredentials) _send_message(client) calls = cast("list[MockRequestCall]", respx_mock.calls) token_calls = [c for c in calls if TOKEN_ENDPOINT in str(c.request.url)] msg_calls = [c for c in calls if str(c.request.url).endswith("/v1/messages")] assert len(token_calls) == 1 assert json.loads(token_calls[0].request.content)["assertion"] == "env-jwt" assert msg_calls[0].request.headers["Authorization"] == "Bearer sk-ant-oat01-test" @pytest.mark.respx() def test_zero_config_credentials_file_from_env( self, respx_mock: MockRouter, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path ) -> None: # no_default_creds_file patched _config_dir → empty dir; re-point at tmp_path clean_env.setattr("anthropic.lib.credentials._constants._config_dir", lambda: tmp_path) clean_env.setenv("ANTHROPIC_CONFIG_DIR", str(tmp_path)) _write_profile(tmp_path, "default", {"type": "external"}, {"access_token": "sk-ant-oat01-file"}) _mock_messages_endpoint(respx_mock) client = Anthropic() assert isinstance(client.credentials, CredentialsFile) _send_message(client) req = cast("list[MockRequestCall]", respx_mock.calls)[0].request assert req.headers["Authorization"] == "Bearer sk-ant-oat01-file" def test_profile_base_url_adopted_by_client(self, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path) -> None: """Outbound: a zero-arg ``Anthropic()`` adopts the active profile's ``base_url`` when the user supplied neither ``base_url=`` nor ``ANTHROPIC_BASE_URL``. Precedence: kwarg > env > profile > default.""" clean_env.delenv("ANTHROPIC_BASE_URL", raising=False) clean_env.setattr("anthropic.lib.credentials._constants._config_dir", lambda: tmp_path) clean_env.setenv("ANTHROPIC_CONFIG_DIR", str(tmp_path)) _write_profile( tmp_path, "default", {"type": "external", "base_url": "https://staging.example"}, {"access_token": "sk-ant-oat01-x"}, ) # profile base_url → client.base_url assert str(Anthropic().base_url).rstrip("/") == "https://staging.example" assert str(AsyncAnthropic().base_url).rstrip("/") == "https://staging.example" # ANTHROPIC_BASE_URL beats profile clean_env.setenv("ANTHROPIC_BASE_URL", "https://env.example") assert str(Anthropic().base_url).rstrip("/") == "https://env.example" clean_env.delenv("ANTHROPIC_BASE_URL", raising=False) # base_url= kwarg beats profile assert str(Anthropic(base_url="https://kwarg.example").base_url).rstrip("/") == "https://kwarg.example" # profile without base_url → hardcoded default _write_profile( tmp_path, "default", {"type": "external"}, {"access_token": "sk-ant-oat01-x"}, ) assert str(Anthropic().base_url).rstrip("/") == "https://api.anthropic.com" def test_config_dict_base_url_adopted_by_client( self, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path ) -> None: """Outbound, ``config=`` path: ``Anthropic(config={"base_url": ...})`` adopts the dict's ``base_url`` for API requests when no kwarg/env is set, mirroring the disk-profile behaviour.""" clean_env.delenv("ANTHROPIC_BASE_URL", raising=False) creds_path = tmp_path / "creds.json" creds_path.write_text(json.dumps({"type": "oauth_token", "access_token": "sk-ant-oat01-x"})) creds_path.chmod(0o600) cfg = { "base_url": "https://staging.example", "authentication": {"type": "user_oauth", "credentials_path": str(creds_path)}, } assert str(Anthropic(config=cfg).base_url).rstrip("/") == "https://staging.example" assert str(AsyncAnthropic(config=cfg).base_url).rstrip("/") == "https://staging.example" # explicit base_url= still wins over config dict assert ( str(Anthropic(config=cfg, base_url="https://kwarg.example").base_url).rstrip("/") == "https://kwarg.example" ) @pytest.mark.respx() def test_workspace_id_header_from_config( self, respx_mock: MockRouter, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path ) -> None: """workspace_id in the config file → anthropic-workspace-id header on API requests.""" clean_env.setattr("anthropic.lib.credentials._constants._config_dir", lambda: tmp_path) clean_env.setenv("ANTHROPIC_CONFIG_DIR", str(tmp_path)) _write_profile( tmp_path, "default", {"type": "external", "workspace_id": "wrkspc_01abc"}, {"access_token": "sk-ant-oat01-file"}, ) _mock_messages_endpoint(respx_mock) client = Anthropic() _send_message(client) req = cast("list[MockRequestCall]", respx_mock.calls)[0].request assert req.headers["anthropic-workspace-id"] == "wrkspc_01abc" @pytest.mark.respx() def test_401_invalidates_cache_and_retries_once(self, respx_mock: MockRouter) -> None: """On 401 we invalidate the cache and retry the current request once with a freshly minted token. A second 401 is not retried (single-shot guard via x-stainless-retry-count).""" provider_calls: List[str] = [] def provider(*, force_refresh: bool = False) -> AccessToken: # noqa: ARG001 provider_calls.append("called") return AccessToken(token=f"tok-{len(provider_calls)}", expires_at=None) # 401 then 200 — the retry should succeed transparently. respx_mock.post(f"{BASE_URL}/v1/messages").mock( side_effect=[ httpx.Response(401, json={"error": "unauthorized"}), httpx.Response( 200, json={ "id": "msg_01", "type": "message", "role": "assistant", "model": "claude-opus-4-5", "content": [{"type": "text", "text": "hi"}], "stop_reason": "end_turn", "stop_sequence": None, "usage": {"input_tokens": 1, "output_tokens": 1}, }, ), ], ) client = Anthropic(credentials=provider, max_retries=2) _send_message(client) assert len(provider_calls) == 2 calls = cast("list[MockRequestCall]", respx_mock.calls) assert calls[0].request.headers["Authorization"] == "Bearer tok-1" assert calls[1].request.headers["Authorization"] == "Bearer tok-2" @pytest.mark.respx() def test_401_retry_is_single_shot(self, respx_mock: MockRouter) -> None: """Two consecutive 401s → exactly one retry, then the error surfaces even with max_retries > 1 remaining.""" respx_mock.post(f"{BASE_URL}/v1/messages").mock( return_value=httpx.Response(401, json={"error": "unauthorized"}), ) provider_calls: List[str] = [] def provider(*, force_refresh: bool = False) -> AccessToken: # noqa: ARG001 provider_calls.append("called") return AccessToken(token=f"tok-{len(provider_calls)}", expires_at=None) client = Anthropic(credentials=provider, max_retries=3) with pytest.raises(anthropic.AuthenticationError): _send_message(client) assert len(provider_calls) == 2 # initial + one retry assert len(cast("list[MockRequestCall]", respx_mock.calls)) == 2 @pytest.mark.respx() def test_api_key_precedence_preserved( self, respx_mock: MockRouter, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path ) -> None: # ANTHROPIC_API_KEY takes precedence over the credential chain — credentials # stays None and X-Api-Key is used (existing behavior preserved). clean_env.setenv("ANTHROPIC_API_KEY", "sk-ant-api-key") # also set chain env vars; they should be ignored clean_env.setenv("ANTHROPIC_CONFIG_DIR", str(tmp_path)) _write_profile(tmp_path, "default", {"type": "external"}, {"access_token": "should-not-be-used"}) _mock_messages_endpoint(respx_mock) client = Anthropic() assert client.credentials is None assert client._token_cache is None _send_message(client) req = cast("list[MockRequestCall]", respx_mock.calls)[0].request assert req.headers["X-Api-Key"] == "sk-ant-api-key" assert "Authorization" not in req.headers def test_copy_propagates_credentials(self) -> None: creds = StaticToken("a") client = Anthropic(credentials=creds) copied = client.copy() assert copied.credentials is creds # The TokenCache instance is shared so a with_options() copy doesn't # trigger an independent token exchange. assert copied._token_cache is client._token_cache other = StaticToken("b") copied2 = client.copy(credentials=other) assert copied2.credentials is other assert copied2._token_cache is not client._token_cache cleared = client.copy(credentials=None, api_key="x") assert cleared.credentials is None assert cleared._token_cache is None def test_with_options_alias(self) -> None: client = Anthropic(credentials=StaticToken("a")) copied = client.with_options(max_retries=7) assert copied.max_retries == 7 assert copied.credentials is client.credentials @pytest.mark.respx() def test_config_param_builds_in_memory_federation(self, respx_mock: MockRouter, tmp_path: pathlib.Path) -> None: """``Anthropic(config={...})`` accepts a config-file-shaped dict and wires it through to a federation provider, including ``workspace_id`` as a default header.""" jwt_path = tmp_path / "jwt" jwt_path.write_text("ext-jwt-value") _mock_token_endpoint(respx_mock) _mock_messages_endpoint(respx_mock) client = Anthropic( config={ "organization_id": "org_x", "workspace_id": "wrkspc_x", "authentication": { "type": "oidc_federation", "federation_rule_id": "fdrl_x", "identity_token": {"source": "file", "path": str(jwt_path)}, }, } ) assert isinstance(client.credentials, InMemoryConfig) _send_message(client) msg_req = cast("list[MockRequestCall]", respx_mock.calls)[-1].request assert msg_req.headers["Authorization"] == "Bearer sk-ant-oat01-test" # Federation profiles send workspace_id in the exchange body, not as a header. assert "anthropic-workspace-id" not in msg_req.headers token_req = cast("list[MockRequestCall]", respx_mock.calls)[0].request assert json.loads(token_req.content)["workspace_id"] == "wrkspc_x" def test_config_and_credentials_mutually_exclusive(self) -> None: with pytest.raises(TypeError, match="at most one of"): Anthropic( credentials=StaticToken("a"), config={"authentication": {"type": "oidc_federation"}}, ) def test_explicit_api_key_shadows_explicit_config(self, tmp_path: pathlib.Path) -> None: """Explicit ``api_key=`` + explicit ``config=`` is an explicit-explicit shadow case: the static api_key wins at the header level and the config-derived credentials provider is silently disabled. """ jwt_path = tmp_path / "jwt" jwt_path.write_text("ext-jwt.ext-jwt.ext-jwt") cfg = { "organization_id": "org_x", "authentication": { "type": "oidc_federation", "federation_rule_id": "fdrl_x", "identity_token": {"source": "file", "path": str(jwt_path)}, }, } explicit = Anthropic(api_key="sk-explicit", config=cfg) assert explicit.api_key == "sk-explicit" assert isinstance(explicit.credentials, InMemoryConfig) def test_explicit_config_beats_env_api_key(self, tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch) -> None: """Explicit ``config=`` is step 1 and beats env ``ANTHROPIC_API_KEY`` (step 2). The env api_key is ignored entirely and the config-derived credentials provider wins. """ monkeypatch.setenv("ANTHROPIC_API_KEY", "sk-from-env") jwt_path = tmp_path / "jwt" jwt_path.write_text("ext-jwt.ext-jwt.ext-jwt") client = Anthropic( config={ "organization_id": "org_x", "authentication": { "type": "oidc_federation", "federation_rule_id": "fdrl_x", "identity_token": {"source": "file", "path": str(jwt_path)}, }, } ) assert client.api_key is None assert isinstance(client.credentials, InMemoryConfig) def test_profile_param_loads_named_profile(self, tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch) -> None: """``Anthropic(profile="staging")`` loads ``configs/staging.json`` from the config directory, equivalent to setting ``ANTHROPIC_PROFILE``.""" monkeypatch.setattr("anthropic.lib.credentials._constants._config_dir", lambda: tmp_path) _write_profile( tmp_path, "staging", config={"type": "external", "base_url": "https://staging.example"}, credentials={"access_token": "sk-ant-oat01-staging"}, ) client = Anthropic(profile="staging") assert isinstance(client.credentials, CredentialsFile) assert client.credentials.profile == "staging" assert str(client.base_url).rstrip("/") == "https://staging.example" def test_profile_and_config_mutually_exclusive(self) -> None: with pytest.raises(TypeError, match="at most one of"): Anthropic(profile="x", config={"authentication": {"type": "oidc_federation"}}) def test_profile_and_credentials_mutually_exclusive(self) -> None: with pytest.raises(TypeError, match="at most one of"): Anthropic(profile="x", credentials=StaticToken("a")) def test_explicit_profile_beats_env_api_key(self, tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch) -> None: """Explicit ``profile=`` is a constructor argument and beats env ``ANTHROPIC_API_KEY`` — the env var is not consulted.""" monkeypatch.setattr("anthropic.lib.credentials._constants._config_dir", lambda: tmp_path) monkeypatch.setenv("ANTHROPIC_API_KEY", "sk-from-env") _write_profile(tmp_path, "dev", config={"type": "external"}, credentials={"access_token": "sk-ant-oat01-dev"}) client = Anthropic(profile="dev") assert client.api_key is None assert isinstance(client.credentials, CredentialsFile) assert client.credentials.profile == "dev" def test_workload_identity_error_propagates_through_request_flow(self) -> None: """A WorkloadIdentityError raised from the credential provider must bubble out of messages.create() as-is, not wrapped in APIConnectionError, and must not trigger retries.""" provider_calls: List[str] = [] def failing_provider(*, force_refresh: bool = False) -> AccessToken: # noqa: ARG001 provider_calls.append("called") raise WorkloadIdentityError( "simulated 403", status_code=403, body={"error": {"type": "permission_error", "message": "Permission denied"}}, ) client = Anthropic(credentials=failing_provider, max_retries=3) with pytest.raises(WorkloadIdentityError) as exc_info: _send_message(client) assert exc_info.value.status_code == 403 body = cast("Dict[str, Any]", exc_info.value.body) assert body["error"]["type"] == "permission_error" assert len(provider_calls) == 1 class TestInMemoryConfig: @pytest.mark.respx() def test_oidc_federation(self, respx_mock: MockRouter, tmp_path: pathlib.Path) -> None: jwt_path = tmp_path / "jwt" jwt_path.write_text("ext-jwt") respx_mock.post(TOKEN_URL).mock( return_value=httpx.Response(200, json={"access_token": "tok", "expires_in": 600}) ) provider = InMemoryConfig( { "organization_id": "org_x", "workspace_id": "wrkspc_x", "authentication": { "type": "oidc_federation", "federation_rule_id": "fdrl_x", "service_account_id": "svac_x", "identity_token": {"source": "file", "path": str(jwt_path)}, }, } ) token = provider() assert token.token == "tok" # Federation profiles send workspace_id in the exchange body, not as a header. assert provider.extra_headers() == {} body = json.loads(cast("list[MockRequestCall]", respx_mock.calls)[0].request.content) assert body["federation_rule_id"] == "fdrl_x" assert body["organization_id"] == "org_x" assert body["service_account_id"] == "svac_x" assert body["workspace_id"] == "wrkspc_x" def test_identity_token_provider_override(self, respx_mock: MockRouter) -> None: respx_mock.post(TOKEN_URL).mock( return_value=httpx.Response(200, json={"access_token": "tok", "expires_in": 600}) ) provider = InMemoryConfig( { "organization_id": "org_x", "workspace_id": "wrkspc_x", "authentication": {"type": "oidc_federation", "federation_rule_id": "fdrl_x"}, }, identity_token_provider=lambda: "programmatic-jwt", ) provider() body = json.loads(cast("list[MockRequestCall]", respx_mock.calls)[0].request.content) assert body["assertion"] == "programmatic-jwt" # workspace_id flows through InMemoryConfig._build_workload_delegate # even when identity_token_provider is overridden — that branch builds # the delegate independently of the file-backed CredentialsFile path. assert body["workspace_id"] == "wrkspc_x" @pytest.mark.respx() def test_oidc_federation_no_credentials_path_no_disk_cache( self, respx_mock: MockRouter, tmp_path: pathlib.Path ) -> None: """Without ``authentication.credentials_path``, every call exchanges fresh — nothing is written to disk.""" token_route = respx_mock.post(TOKEN_URL).mock( return_value=httpx.Response(200, json={"access_token": "tok", "expires_in": 600}) ) provider = InMemoryConfig( { "organization_id": "org_x", "authentication": {"type": "oidc_federation", "federation_rule_id": "fdrl_x"}, }, identity_token_provider=lambda: "jwt", ) provider() provider() assert token_route.call_count == 2 assert not list(tmp_path.glob("**/*.json")) @pytest.mark.respx() def test_oidc_federation_with_credentials_path_disk_cache( self, respx_mock: MockRouter, tmp_path: pathlib.Path ) -> None: """With ``authentication.credentials_path`` set, the exchanged token is written to that path and a second call returns it without re-exchanging.""" creds_path = tmp_path / "cache.json" token_route = respx_mock.post(TOKEN_URL).mock( return_value=httpx.Response(200, json={"access_token": "cached-tok", "expires_in": 600}) ) provider = InMemoryConfig( { "organization_id": "org_x", "authentication": { "type": "oidc_federation", "federation_rule_id": "fdrl_x", "credentials_path": str(creds_path), }, }, identity_token_provider=lambda: "jwt", ) assert provider().token == "cached-tok" assert token_route.call_count == 1 on_disk = json.loads(creds_path.read_text()) assert on_disk["access_token"] == "cached-tok" assert on_disk["type"] == "oauth_token" if os.name == "posix": assert (creds_path.stat().st_mode & 0o777) == 0o600 # Second call hits disk cache, not network. assert provider().token == "cached-tok" assert token_route.call_count == 1 def test_user_oauth_requires_credentials_path(self) -> None: with pytest.raises(AnthropicError, match="requires 'authentication.credentials_path'"): InMemoryConfig({"authentication": {"type": "user_oauth", "client_id": "cid"}}) @pytest.mark.respx() def test_user_oauth_refresh_and_writeback(self, respx_mock: MockRouter, tmp_path: pathlib.Path) -> None: """user_oauth with ``credentials_path`` runs the refresh-token grant on expiry and writes the new tokens back, exactly like a file-backed ``CredentialsFile`` profile.""" creds_path = tmp_path / "creds.json" creds_path.write_text( json.dumps( { "type": "oauth_token", "access_token": "old-tok", "expires_at": int(time.time()) - 1, "refresh_token": "refresh-old", } ) ) creds_path.chmod(0o600) refresh_route = respx_mock.post(TOKEN_URL).mock( return_value=httpx.Response( 200, json={"access_token": "new-tok", "expires_in": 3600, "refresh_token": "refresh-new"} ) ) provider = InMemoryConfig( { "workspace_id": "wrkspc_x", "authentication": { "type": "user_oauth", "client_id": "cid", "credentials_path": str(creds_path), }, } ) tok = provider() assert tok.token == "new-tok" assert refresh_route.call_count == 1 rewritten = json.loads(creds_path.read_text()) assert rewritten["access_token"] == "new-tok" assert rewritten["refresh_token"] == "refresh-new" # workspace-id header IS set for user_oauth (federation suppresses it). assert provider.extra_headers() == {"anthropic-workspace-id": "wrkspc_x"} def test_unknown_type_rejected(self) -> None: with pytest.raises(AnthropicError, match="Unknown authentication.type"): InMemoryConfig({"authentication": {"type": "something_else"}}) def test_missing_authentication(self) -> None: with pytest.raises(AnthropicError, match="missing the 'authentication' object"): InMemoryConfig({"organization_id": "org_x"}) def test_missing_required_fields(self) -> None: provider = InMemoryConfig( {"authentication": {"type": "oidc_federation"}}, identity_token_provider=lambda: "j", ) with pytest.raises(WorkloadIdentityError, match="federation_rule_id"): provider() def test_http_base_url_rejected(self) -> None: with pytest.raises(AnthropicError, match="must use https"): InMemoryConfig( { "organization_id": "org_x", "base_url": "http://example.com", "authentication": {"type": "oidc_federation", "federation_rule_id": "fdrl_x"}, }, identity_token_provider=lambda: "j", ) async def _send_message_async(client: AsyncAnthropic) -> None: await client.messages.create( max_tokens=1, model="claude-opus-4-5", messages=[{"role": "user", "content": "hi"}], ) @pytest.mark.usefixtures("clean_env", "no_default_creds_file") class TestAsyncAnthropicCredentials: @pytest.mark.respx() async def test_async_static_token(self, respx_mock: MockRouter) -> None: _mock_messages_endpoint(respx_mock) client = AsyncAnthropic(credentials=StaticToken("async-bearer")) await _send_message_async(client) req = cast("list[MockRequestCall]", respx_mock.calls)[0].request assert req.headers["Authorization"] == "Bearer async-bearer" assert OAUTH_API_BETA_HEADER in req.headers["anthropic-beta"] @pytest.mark.respx() async def test_async_workload_identity_exchange(self, respx_mock: MockRouter) -> None: """Async client exchanges the OIDC JWT via the token endpoint and attaches the resulting Bearer token to the request.""" _mock_token_endpoint(respx_mock) _mock_messages_endpoint(respx_mock) client = AsyncAnthropic( credentials=WorkloadIdentityCredentials( identity_token_provider=lambda: "ext-jwt", federation_rule_id="fdrl_01abc", organization_id="org-uuid", ), ) await _send_message_async(client) msg_calls = [ c for c in cast("list[MockRequestCall]", respx_mock.calls) if str(c.request.url).endswith("/v1/messages") ] assert len(msg_calls) == 1 assert msg_calls[0].request.headers["Authorization"] == "Bearer sk-ant-oat01-test" @pytest.mark.respx() async def test_async_authorized_user_refresh_flow( self, respx_mock: MockRouter, tmp_path: pathlib.Path, monkeypatch: pytest.MonkeyPatch, ) -> None: """Async client calling a CredentialsFile(authorized_user) provider: the blocking refresh_token POST runs on the worker thread via asyncify.""" monkeypatch.setattr("anthropic.lib.credentials._constants._config_dir", lambda: tmp_path) _write_profile( tmp_path, "default", {"type": "authorized_user", "client_id": "cid"}, {"access_token": "old", "expires_at": int(time.time()) - 1, "refresh_token": "rt"}, ) respx_mock.post(TOKEN_URL).mock( return_value=httpx.Response(200, json={"access_token": "refreshed", "expires_in": 3600}) ) _mock_messages_endpoint(respx_mock) client = AsyncAnthropic(credentials=CredentialsFile()) await _send_message_async(client) msg_calls = [ c for c in cast("list[MockRequestCall]", respx_mock.calls) if str(c.request.url).endswith("/v1/messages") ] assert msg_calls[0].request.headers["Authorization"] == "Bearer refreshed" @pytest.mark.respx() async def test_async_concurrent_requests_single_flight(self, respx_mock: MockRouter) -> None: """Concurrent async requests sharing a TokenCache cause at most one provider call — the single-flight guarantee holds across async workers.""" _mock_messages_endpoint(respx_mock) calls: List[int] = [] def provider(*, force_refresh: bool = False) -> AccessToken: # noqa: ARG001 calls.append(1) time.sleep(0.05) # widen the window for racing workers return AccessToken(token=f"tok-{len(calls)}", expires_at=None) import asyncio client = AsyncAnthropic(credentials=provider) await asyncio.gather(*(_send_message_async(client) for _ in range(8))) # Exactly one provider call across eight concurrent requests. assert len(calls) == 1 @pytest.mark.respx() async def test_async_401_invalidates_and_retries_once(self, respx_mock: MockRouter) -> None: """Async client 401 behavior mirrors sync: invalidate the cache and retry the current request once with a freshly minted token.""" respx_mock.post(f"{BASE_URL}/v1/messages").mock( return_value=httpx.Response(401, json={"error": "unauthorized"}), ) provider_calls: List[str] = [] def provider(*, force_refresh: bool = False) -> AccessToken: # noqa: ARG001 provider_calls.append("called") return AccessToken(token=f"tok-{len(provider_calls)}", expires_at=None) client = AsyncAnthropic(credentials=provider, max_retries=2) with pytest.raises(anthropic.AuthenticationError): await _send_message_async(client) assert len(provider_calls) == 2 async def test_async_close_cascades_to_credentials(self) -> None: """AsyncAnthropic.close() cascades to self.credentials.close().""" class TrackedProvider: closed = False def __call__(self, *, force_refresh: bool = False) -> AccessToken: # noqa: ARG002 return AccessToken(token="t", expires_at=None) def close(self) -> None: self.closed = True tracked = TrackedProvider() client = AsyncAnthropic(credentials=tracked) await client.close() assert tracked.closed async def test_async_aclose_via_context_manager(self) -> None: """`async with AsyncAnthropic(...)` exits cleanly and cascades close.""" class TrackedProvider: closed = False def __call__(self, *, force_refresh: bool = False) -> AccessToken: # noqa: ARG002 return AccessToken(token="t", expires_at=None) def close(self) -> None: self.closed = True tracked = TrackedProvider() async with AsyncAnthropic(credentials=tracked) as _client: pass assert tracked.closed @pytest.mark.respx() async def test_async_max_retries_zero_honored_on_401(self, respx_mock: MockRouter) -> None: """max_retries=0 means a 401 surfaces immediately — no implicit retry.""" respx_mock.post(f"{BASE_URL}/v1/messages").mock( return_value=httpx.Response(401, json={"error": "unauthorized"}), ) client = AsyncAnthropic(credentials=StaticToken("t"), max_retries=0) with pytest.raises(anthropic.AuthenticationError): await _send_message_async(client) # Exactly one request attempt, not two. assert len(cast("list[MockRequestCall]", respx_mock.calls)) == 1 async def test_async_workload_identity_error_propagates_through_request_flow(self) -> None: """Async counterpart of the sync regression test: a WorkloadIdentityError raised by the credential provider must bubble out of the async messages.create() as-is, not wrapped in APIConnectionError, and must not trigger retries.""" provider_calls: List[str] = [] def failing_provider(*, force_refresh: bool = False) -> AccessToken: # noqa: ARG001 provider_calls.append("called") raise WorkloadIdentityError( "simulated 403", status_code=403, body={"error": {"type": "permission_error", "message": "Permission denied"}}, ) client = AsyncAnthropic(credentials=failing_provider, max_retries=3) with pytest.raises(WorkloadIdentityError) as exc_info: await _send_message_async(client) assert exc_info.value.status_code == 403 body = cast("Dict[str, Any]", exc_info.value.body) assert body["error"]["type"] == "permission_error" assert len(provider_calls) == 1 @pytest.mark.usefixtures("clean_env", "no_default_creds_file") class TestTypedCredentialErrors: """Every exit point in the credentials subsystem raises an ``AnthropicError`` (or subclass). Anything outside that hierarchy is wrapped as ``APIConnectionError`` and retried by the base client's ``except Exception`` handler, hiding the real cause and amplifying load. """ def test_invalid_profile_name_raises_anthropic_error( self, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path ) -> None: clean_env.setattr("anthropic.lib.credentials._constants._config_dir", lambda: tmp_path) clean_env.setenv("ANTHROPIC_PROFILE", "work/dev") with pytest.raises(AnthropicError, match="ANTHROPIC_PROFILE"): Anthropic() def test_profile_name_with_dot_prefix_raises_anthropic_error( self, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path ) -> None: clean_env.setattr("anthropic.lib.credentials._constants._config_dir", lambda: tmp_path) clean_env.setenv("ANTHROPIC_PROFILE", ".hidden") with pytest.raises(AnthropicError, match="start with a dot"): Anthropic() def test_http_base_url_on_workload_provider_raises_anthropic_error(self) -> None: creds = WorkloadIdentityCredentials( identity_token_provider=lambda: "jwt", federation_rule_id="fdrl_x", organization_id="org_x", ) with pytest.raises(AnthropicError, match="https"): creds.bind_base_url("http://evil.example/") def test_http_base_url_in_config_file_raises_anthropic_error( self, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path ) -> None: clean_env.setattr("anthropic.lib.credentials._constants._config_dir", lambda: tmp_path) _write_profile( tmp_path, "default", { "base_url": "http://api.example/", "authentication": {"type": "user_oauth"}, }, {"access_token": "tok"}, ) with pytest.raises(AnthropicError, match="https"): CredentialsFile("default")() def test_identity_token_file_permission_error_raises_anthropic_error(self, tmp_path: pathlib.Path) -> None: if os.name != "posix": pytest.skip("chmod semantics only apply on POSIX") if os.geteuid() == 0: pytest.skip("root bypasses POSIX mode bits") f = tmp_path / "token" f.write_text("jwt") f.chmod(0o000) try: provider = IdentityTokenFile(f) with pytest.raises(AnthropicError, match="not readable|Permission"): provider() finally: f.chmod(0o600) def test_identity_token_file_directory_raises_anthropic_error(self, tmp_path: pathlib.Path) -> None: provider = IdentityTokenFile(tmp_path) with pytest.raises(AnthropicError): provider() def test_identity_token_file_binary_content_raises_anthropic_error(self, tmp_path: pathlib.Path) -> None: f = tmp_path / "token" f.write_bytes(b"\xff\xfe\xfd\x00not-utf8") provider = IdentityTokenFile(f) with pytest.raises(AnthropicError): provider() def test_identity_token_file_empty_raises_anthropic_error(self, tmp_path: pathlib.Path) -> None: f = tmp_path / "token" f.write_text("") provider = IdentityTokenFile(f) with pytest.raises(AnthropicError, match="empty"): provider() def test_user_oauth_malformed_expires_at_raises_anthropic_error( self, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path ) -> None: """An ISO8601 string in ``expires_at`` must raise AnthropicError naming the expected shape.""" clean_env.setattr("anthropic.lib.credentials._constants._config_dir", lambda: tmp_path) _write_profile( tmp_path, "default", {"authentication": {"type": "user_oauth"}}, {"access_token": "tok", "expires_at": "2030-01-01T00:00:00Z"}, ) with pytest.raises(AnthropicError, match="expires_at"): CredentialsFile("default")() class TestTokenCacheDeadlock: def test_non_anthropic_error_from_provider_releases_waiters(self) -> None: """A non-``AnthropicError`` / non-``httpx.HTTPError`` from the leader provider must still release ``_refresh_event`` so concurrent waiters don't deadlock.""" import threading as _threading ready = _threading.Event() release_leader = _threading.Event() class FlakyProvider: calls = 0 def __call__(self, *, force_refresh: bool = False) -> AccessToken: # noqa: ARG002 self.calls += 1 if self.calls == 1: ready.set() release_leader.wait(timeout=2) raise RuntimeError("programmer error from a custom provider") return AccessToken("fresh", expires_at=None) provider = FlakyProvider() cache = TokenCache(provider) leader_error: List[BaseException] = [] def run_leader() -> None: try: cache.get_token() except BaseException as err: leader_error.append(err) leader = _threading.Thread(target=run_leader, daemon=True) leader.start() assert ready.wait(timeout=2) waiter_error: List[BaseException] = [] waiter_result: List[str] = [] def run_waiter() -> None: try: waiter_result.append(cache.get_token()) except BaseException as err: waiter_error.append(err) waiter = _threading.Thread(target=run_waiter, daemon=True) waiter.start() release_leader.set() leader.join(timeout=5) assert not leader.is_alive(), "leader deadlocked after provider raised" waiter.join(timeout=5) assert not waiter.is_alive(), "waiter deadlocked after leader failed" assert len(leader_error) == 1 assert isinstance(leader_error[0], RuntimeError) assert cache.get_token() == "fresh" def test_value_error_from_provider_propagates_cleanly(self) -> None: """A ``ValueError`` (e.g. from a provider that parses a JWT) escapes but the cache state is clean — the next call succeeds without hanging.""" class P: calls = 0 def __call__(self, *, force_refresh: bool = False) -> AccessToken: # noqa: ARG002 self.calls += 1 if self.calls == 1: raise ValueError("malformed assertion") return AccessToken("ok", expires_at=None) cache = TokenCache(P()) with pytest.raises(ValueError, match="malformed assertion"): cache.get_token() assert cache.get_token() == "ok" @pytest.mark.usefixtures("clean_env", "no_default_creds_file") class TestCredentialPrecedence: """Credential precedence per the WIF user guide and the credential- resolution spec: explicit ctor args (step 1) beat env vars (step 2), which beat profile / federation auto-discovery (steps 3-5). A static env credential (step 2) shadows auto-discovery (steps 3-5), silently disabling profile / federation — we warn about that. It does NOT shadow an explicit ``credentials=`` argument (step 1): an explicit credentials provider wins over env ``ANTHROPIC_API_KEY`` / ``ANTHROPIC_AUTH_TOKEN`` outright. Passing an explicit ``api_key=`` or ``auth_token=`` *argument* alongside an explicit ``credentials=`` is a separate shadow case: the static credential wins at the header level and we warn. """ @pytest.fixture(autouse=True) def _reset_shadow_one_shot(self) -> None: from anthropic.lib.credentials import _auth _auth._warn_once_seen.clear() @staticmethod def _walk_sync_auth(client: Anthropic) -> httpx.Request: request = client._build_request(FinalRequestOptions(method="get", url="/foo")) auth = client.custom_auth flow = auth.sync_auth_flow(request) if auth is not None else None if flow is None: return request modified = next(flow) try: flow.send(httpx.Response(200)) except StopIteration: pass return modified # -- step 1 beats step 2: explicit credentials= beats env static -------- def test_explicit_credentials_beats_env_api_key( self, clean_env: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture ) -> None: """Per spec, explicit ``credentials=`` is step 1 and beats env ``ANTHROPIC_API_KEY`` (step 2). The credentials provider wins, env api_key is ignored entirely, no X-Api-Key on the wire, no warning.""" clean_env.setenv("ANTHROPIC_API_KEY", "sk-from-env") with caplog.at_level(logging.WARNING, logger="anthropic.lib.credentials._auth"): client = Anthropic(credentials=StaticToken("bearer-from-creds")) assert client.api_key is None assert client.auth_token is None assert client.credentials is not None req = self._walk_sync_auth(client) assert req.headers.get("X-Api-Key") is None assert req.headers.get("Authorization") == "Bearer bearer-from-creds" assert not any("takes precedence" in r.message for r in caplog.records) def test_explicit_credentials_beats_env_auth_token( self, clean_env: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture ) -> None: clean_env.setenv("ANTHROPIC_AUTH_TOKEN", "env-auth-token") with caplog.at_level(logging.WARNING, logger="anthropic.lib.credentials._auth"): client = Anthropic(credentials=StaticToken("bearer-from-creds")) assert client.auth_token is None assert client.credentials is not None req = self._walk_sync_auth(client) assert req.headers.get("Authorization") == "Bearer bearer-from-creds" assert not any("takes precedence" in r.message for r in caplog.records) def test_explicit_config_beats_env_api_key( self, clean_env: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture, tmp_path: pathlib.Path ) -> None: """Explicit ``config=`` is also step 1 and beats env api_key.""" jwt = tmp_path / "jwt" jwt.write_text("ext-jwt.ext-jwt.ext-jwt") clean_env.setenv("ANTHROPIC_API_KEY", "sk-from-env") with caplog.at_level(logging.WARNING, logger="anthropic.lib.credentials._auth"): client = Anthropic( config={ "organization_id": "org_x", "authentication": { "type": "oidc_federation", "federation_rule_id": "fdrl_x", "identity_token": {"source": "file", "path": str(jwt)}, }, } ) assert client.api_key is None assert isinstance(client.credentials, InMemoryConfig) # -- step 1 ∩ step 1: explicit static arg + explicit credentials= -------- def test_explicit_api_key_shadows_explicit_credentials_with_warning(self, caplog: pytest.LogCaptureFixture) -> None: """When both explicit ``api_key=`` AND explicit ``credentials=`` are passed, the static api_key wins at the header level and credentials is silently disabled. Warn.""" with caplog.at_level(logging.WARNING, logger="anthropic.lib.credentials._auth"): client = Anthropic(api_key="sk-explicit", credentials=StaticToken("bearer-from-creds")) req = self._walk_sync_auth(client) assert req.headers.get("X-Api-Key") == "sk-explicit" assert req.headers.get("Authorization") is None assert any("`api_key=`" in r.message for r in caplog.records) def test_explicit_auth_token_shadows_explicit_credentials_with_warning( self, caplog: pytest.LogCaptureFixture ) -> None: with caplog.at_level(logging.WARNING, logger="anthropic.lib.credentials._auth"): client = Anthropic(auth_token="static-bearer", credentials=StaticToken("bearer-from-creds")) req = self._walk_sync_auth(client) assert req.headers.get("Authorization") == "Bearer static-bearer" assert req.headers.get("X-Api-Key") is None assert any("`auth_token=`" in r.message for r in caplog.records) def test_async_explicit_api_key_shadows_explicit_credentials(self, caplog: pytest.LogCaptureFixture) -> None: with caplog.at_level(logging.WARNING, logger="anthropic.lib.credentials._auth"): client = AsyncAnthropic(api_key="sk-explicit", credentials=StaticToken("bearer-from-creds")) assert client.api_key == "sk-explicit" assert client.credentials is not None assert any("`api_key=`" in r.message for r in caplog.records) def test_copy_with_explicit_api_key_shadows_inherited_credentials(self, caplog: pytest.LogCaptureFixture) -> None: """Reviewer ask: copy() should warn when a new explicit ``api_key=`` shadows an inherited ``credentials=`` provider from the parent.""" parent = Anthropic(credentials=StaticToken("bearer-parent")) assert parent.api_key is None with caplog.at_level(logging.WARNING, logger="anthropic.lib.credentials._auth"): shadowed = parent.copy(api_key="sk-new") assert shadowed.api_key == "sk-new" assert shadowed.credentials is parent.credentials req = self._walk_sync_auth(shadowed) assert req.headers.get("X-Api-Key") == "sk-new" assert req.headers.get("Authorization") is None assert any("`api_key=`" in r.message for r in caplog.records) # -- step 2 shadows steps 3-5: env static shadows auto-discovery --------- def test_env_api_key_shadows_env_federation_trio_with_warning( self, clean_env: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture, tmp_path: pathlib.Path ) -> None: """Env ``ANTHROPIC_API_KEY`` + env federation trio → api_key wins (step 2 beats step 4), warn so the user knows WIF is being shadowed.""" jwt = tmp_path / "jwt" jwt.write_text("ext-jwt.ext-jwt.ext-jwt") clean_env.setenv("ANTHROPIC_API_KEY", "sk-from-env") clean_env.setenv("ANTHROPIC_IDENTITY_TOKEN_FILE", str(jwt)) clean_env.setenv("ANTHROPIC_FEDERATION_RULE_ID", "fdrl_01abc") clean_env.setenv("ANTHROPIC_ORGANIZATION_ID", "00000000-0000-0000-0000-000000000000") with caplog.at_level(logging.WARNING, logger="anthropic.lib.credentials._auth"): client = Anthropic() assert client.api_key == "sk-from-env" assert client.credentials is None assert any("ANTHROPIC_API_KEY" in r.message and "profile / federation" in r.message for r in caplog.records) def test_env_api_key_shadows_env_profile_with_warning( self, clean_env: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture ) -> None: clean_env.setenv("ANTHROPIC_API_KEY", "sk-from-env") clean_env.setenv("ANTHROPIC_PROFILE", "dev") with caplog.at_level(logging.WARNING, logger="anthropic.lib.credentials._auth"): client = Anthropic() assert client.api_key == "sk-from-env" assert client.credentials is None assert any("ANTHROPIC_API_KEY" in r.message and "profile / federation" in r.message for r in caplog.records) def test_env_api_key_alone_does_not_warn( self, clean_env: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture ) -> None: """No shadow if there's nothing to shadow — env api_key alone (no auto-discoverable credential signals) is just the normal path.""" clean_env.setenv("ANTHROPIC_API_KEY", "sk-from-env") with caplog.at_level(logging.WARNING, logger="anthropic.lib.credentials._auth"): client = Anthropic() assert client.api_key == "sk-from-env" assert not any("takes precedence" in r.message for r in caplog.records) # -- step 1 alone: credentials= works normally -------------------------- def test_credentials_only_still_works(self, caplog: pytest.LogCaptureFixture) -> None: with caplog.at_level(logging.WARNING, logger="anthropic.lib.credentials._auth"): client = Anthropic(credentials=StaticToken("bearer-from-creds")) req = self._walk_sync_auth(client) assert req.headers.get("X-Api-Key") is None assert req.headers.get("Authorization") == "Bearer bearer-from-creds" assert not any("takes precedence" in r.message for r in caplog.records) # -- one-shot warning --------------------------------------------------- def test_shadow_warning_is_one_shot_per_process( self, clean_env: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture ) -> None: """The shadow warning is emitted once per shadow-key per process so repeated client construction doesn't spam logs.""" clean_env.setenv("ANTHROPIC_API_KEY", "sk-from-env") clean_env.setenv("ANTHROPIC_PROFILE", "dev") with caplog.at_level(logging.WARNING, logger="anthropic.lib.credentials._auth"): Anthropic() Anthropic() Anthropic() shadow_records = [r for r in caplog.records if "takes precedence" in r.message] assert len(shadow_records) == 1 @pytest.mark.usefixtures("clean_env", "no_default_creds_file") class TestDanglingActiveConfig: """``active_config`` pointer file naming a profile with no matching ``configs/.json`` should surface a clear error rather than silently falling through to "no auth configured". """ def test_pointer_at_missing_profile_raises(self, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path) -> None: clean_env.setattr("anthropic.lib.credentials._constants._config_dir", lambda: tmp_path) (tmp_path / "active_config").write_text("prod") with pytest.raises(AnthropicError, match="prod"): default_credentials() def test_pointer_at_missing_profile_raises_via_client( self, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path ) -> None: clean_env.setattr("anthropic.lib.credentials._constants._config_dir", lambda: tmp_path) (tmp_path / "active_config").write_text("prod") with pytest.raises(AnthropicError, match="prod"): Anthropic() def test_empty_pointer_file_is_silent_fallthrough( self, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path ) -> None: clean_env.setattr("anthropic.lib.credentials._constants._config_dir", lambda: tmp_path) (tmp_path / "active_config").write_text("") assert default_credentials() is None def test_pointer_at_present_profile_loads_normally( self, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path ) -> None: clean_env.setattr("anthropic.lib.credentials._constants._config_dir", lambda: tmp_path) (tmp_path / "active_config").write_text("prod") _write_profile(tmp_path, "prod", {"type": "external"}, {"access_token": "from-prod"}) result = default_credentials() assert result is not None assert isinstance(result.provider, CredentialsFile) assert result.provider().token == "from-prod" # --------------------------------------------------------------------------- # # Secret hygiene: masked reprs and traceback frame locals # --------------------------------------------------------------------------- # _SECRET_ASSERTION = "eyJ-SECRET-ASSERTION-MATERIAL-eyJ" _SECRET_MINTED = "sk-ant-oat01-MINTED-SECRET" class TestAccessTokenReprMasking: def test_long_token_masked_to_last_four(self) -> None: tok = AccessToken(token="sk-ant-oat01-SECRETMATERIAL", expires_at=123) assert repr(tok) == "AccessToken(token='...RIAL', expires_at=123)" assert str(tok) == repr(tok) def test_short_token_fully_masked(self) -> None: assert repr(AccessToken(token="hunter2")) == "AccessToken(token='**********', expires_at=None)" def test_non_str_token_does_not_crash_repr(self) -> None: # A malformed token endpoint can produce a non-str token; repr must # not raise (crash reporters call it blindly). assert repr(AccessToken(token=cast(Any, 12345))) == "AccessToken(token='**********', expires_at=None)" class TestEmptySecretFieldFalsiness: """The missing-token guards test truthiness of SecretStr-wrapped values, which rides on ``SecretStr.__len__`` (present across the supported pydantic range) — pin empty-string behavior through the public path so a pydantic regression can't silently turn the guards into passes.""" def test_empty_access_token_treated_as_missing(self, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path) -> None: clean_env.setattr("anthropic.lib.credentials._constants._config_dir", lambda: tmp_path) _write_profile( tmp_path, "default", config={"type": "authorized_user", "client_id": "cid"}, credentials={"access_token": "", "refresh_token": "rt"}, ) with pytest.raises(AnthropicError, match="missing 'access_token'"): CredentialsFile()() def test_empty_refresh_token_treated_as_missing( self, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path ) -> None: clean_env.setattr("anthropic.lib.credentials._constants._config_dir", lambda: tmp_path) _write_profile( tmp_path, "default", config={"type": "authorized_user", "client_id": "cid"}, credentials={"access_token": "at", "refresh_token": ""}, ) with pytest.raises(WorkloadIdentityError, match="must include 'refresh_token'"): CredentialsFile()() def _sdk_frame_locals(exc: BaseException) -> List["tuple[str, Dict[str, Any]]"]: """(code name, locals) for every traceback frame owned by the anthropic package, across the full ``__context__`` / ``__cause__`` chain.""" pkg_root = str(pathlib.Path(anthropic.__file__).parent) out: List["tuple[str, Dict[str, Any]]"] = [] seen: "set[int]" = set() def walk(e: Optional[BaseException]) -> None: if e is None or id(e) in seen: return seen.add(id(e)) tb = e.__traceback__ while tb is not None: code = tb.tb_frame.f_code if code.co_filename.startswith(pkg_root): out.append((code.co_name, dict(tb.tb_frame.f_locals))) tb = tb.tb_next walk(e.__context__) walk(e.__cause__) walk(exc) return out def _assert_not_in_sdk_frame_locals(exc: BaseException, *secrets: str) -> None: frames = _sdk_frame_locals(exc) assert frames, "expected at least one SDK-owned frame in the traceback" for name, frame_locals in frames: for var, value in frame_locals.items(): for secret in secrets: assert secret not in repr(value), f"secret retained in frame {name!r} local {var!r}" class TestNoSecretsInTracebackFrameLocals: """Exceptions from the token-exchange paths must not retain credential material in traceback frame locals (across the ``__traceback__`` / ``__context__`` / ``__cause__`` chain). Plain ``logging.exception`` never prints locals, but crash reporters that capture them — stdlib ``TracebackException(..., capture_locals=True)``, rich tracebacks, Sentry's default local-variable capture — render each local's ``repr``, which is why SecretStr-wrapped locals are safe to retain.""" def _workload_provider(self, handler: Callable[[httpx.Request], httpx.Response]) -> WorkloadIdentityCredentials: creds = WorkloadIdentityCredentials( identity_token_provider=lambda: _SECRET_ASSERTION, federation_rule_id="fdrl_01abc", organization_id="org-uuid", http_client=httpx.Client(transport=httpx.MockTransport(handler)), ) creds.bind_base_url(BASE_URL) return creds def test_http_4xx_does_not_retain_assertion(self) -> None: provider = self._workload_provider(lambda _: httpx.Response(401, json={"error": "invalid_grant"})) with pytest.raises(WorkloadIdentityError) as exc_info: provider() _assert_not_in_sdk_frame_locals(exc_info.value, _SECRET_ASSERTION) def test_invalid_expires_in_does_not_retain_minted_token(self) -> None: provider = self._workload_provider( lambda _: httpx.Response(200, json={"access_token": _SECRET_MINTED, "expires_in": "NaN"}) ) with pytest.raises(WorkloadIdentityError) as exc_info: provider() _assert_not_in_sdk_frame_locals(exc_info.value, _SECRET_ASSERTION, _SECRET_MINTED) def test_token_type_mismatch_does_not_retain_minted_token(self) -> None: provider = self._workload_provider( lambda _: httpx.Response(200, json={"access_token": _SECRET_MINTED, "expires_in": 600, "token_type": "MAC"}) ) with pytest.raises(WorkloadIdentityError) as exc_info: provider() _assert_not_in_sdk_frame_locals(exc_info.value, _SECRET_ASSERTION, _SECRET_MINTED) def test_non_json_response_does_not_retain_assertion(self) -> None: provider = self._workload_provider(lambda _: httpx.Response(200, text="gateway error")) with pytest.raises(WorkloadIdentityError) as exc_info: provider() _assert_not_in_sdk_frame_locals(exc_info.value, _SECRET_ASSERTION) # The json decoder frames hold the raw response text (which token # endpoints can echo the assertion into); the raise site must have # dropped the chained cause's traceback entirely. cause = exc_info.value.__cause__ assert cause is not None and cause.__traceback__ is None def test_json_string_echo_response_does_not_retain_assertion(self) -> None: """A 200 whose body is a JSON *string* echoing the assertion must be rejected at the wrap boundary — never bound to a frame local on its way to the type error.""" provider = self._workload_provider(lambda _: httpx.Response(200, json=_SECRET_ASSERTION)) with pytest.raises(WorkloadIdentityError, match="expected an object") as exc_info: provider() _assert_not_in_sdk_frame_locals(exc_info.value, _SECRET_ASSERTION) def test_transport_error_does_not_retain_assertion(self) -> None: def raise_connect_error(req: httpx.Request) -> httpx.Response: raise httpx.ConnectError("connection refused", request=req) provider = self._workload_provider(raise_connect_error) with pytest.raises(WorkloadIdentityError) as exc_info: provider() _assert_not_in_sdk_frame_locals(exc_info.value, _SECRET_ASSERTION) # The chained httpx error's frames hold the serialized request body; # the raise site must have dropped its traceback entirely. cause = exc_info.value.__cause__ assert cause is not None and cause.__traceback__ is None def test_oversized_assertion_not_retained(self) -> None: big = "A" * (17 * 1024) provider = WorkloadIdentityCredentials( identity_token_provider=lambda: big, federation_rule_id="fdrl_01abc", organization_id="org-uuid", http_client=httpx.Client(), ) with pytest.raises(WorkloadIdentityError) as exc_info: provider() _assert_not_in_sdk_frame_locals(exc_info.value, big) def _write_refresh_profile(self, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path) -> None: clean_env.setattr("anthropic.lib.credentials._constants._config_dir", lambda: tmp_path) _write_profile( tmp_path, "default", config={"type": "authorized_user", "client_id": "cid"}, credentials={ "access_token": "old-access-SECRET", "expires_at": int(time.time()) - 1, "refresh_token": "rt-SECRET", }, ) @pytest.mark.respx(base_url=BASE_URL) def test_refresh_failure_does_not_retain_refresh_token( self, respx_mock: MockRouter, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path ) -> None: self._write_refresh_profile(clean_env, tmp_path) respx_mock.post(TOKEN_ENDPOINT).mock(return_value=httpx.Response(400, json={"error": "invalid_grant"})) with pytest.raises(WorkloadIdentityError) as exc_info: CredentialsFile()() _assert_not_in_sdk_frame_locals(exc_info.value, "rt-SECRET", "old-access-SECRET") @pytest.mark.respx(base_url=BASE_URL) def test_refresh_invalid_expires_does_not_retain_new_token( self, respx_mock: MockRouter, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path ) -> None: self._write_refresh_profile(clean_env, tmp_path) respx_mock.post(TOKEN_ENDPOINT).mock( return_value=httpx.Response(200, json={"access_token": _SECRET_MINTED, "expires_in": "NaN"}) ) with pytest.raises(WorkloadIdentityError) as exc_info: CredentialsFile()() _assert_not_in_sdk_frame_locals(exc_info.value, "rt-SECRET", "old-access-SECRET", _SECRET_MINTED) @pytest.mark.respx(base_url=BASE_URL) def test_refresh_non_json_response_raises_redacted_error( self, respx_mock: MockRouter, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path ) -> None: """A non-JSON refresh response raises WorkloadIdentityError (it previously escaped as a raw json ValueError whose ``.doc`` carries the full response body) and retains no secrets.""" self._write_refresh_profile(clean_env, tmp_path) respx_mock.post(TOKEN_ENDPOINT).mock(return_value=httpx.Response(200, text="gateway error")) with pytest.raises(WorkloadIdentityError, match="non-JSON") as exc_info: CredentialsFile()() _assert_not_in_sdk_frame_locals(exc_info.value, "rt-SECRET", "old-access-SECRET") # As on the jwt-bearer path: the decoder frames hold the raw body. cause = exc_info.value.__cause__ assert cause is not None and cause.__traceback__ is None @pytest.mark.respx(base_url=BASE_URL) def test_refresh_json_array_response_raises_redacted_error( self, respx_mock: MockRouter, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path ) -> None: """A JSON-but-non-object refresh body (which can echo the request's refresh token) is rejected at the wrap boundary and never bound to a frame local.""" self._write_refresh_profile(clean_env, tmp_path) respx_mock.post(TOKEN_ENDPOINT).mock(return_value=httpx.Response(200, json=["rt-SECRET"])) with pytest.raises(WorkloadIdentityError, match="expected an object") as exc_info: CredentialsFile()() _assert_not_in_sdk_frame_locals(exc_info.value, "rt-SECRET", "old-access-SECRET") def test_scalar_credentials_file_does_not_retain_token( self, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path ) -> None: """A credentials file whose top level is a JSON string (e.g. a bare token pasted into the file) must raise a clean error without the value surviving in frame locals.""" clean_env.setattr("anthropic.lib.credentials._constants._config_dir", lambda: tmp_path) _write_profile(tmp_path, "default", config={"type": "authorized_user", "client_id": "cid"}) creds_path = tmp_path / "credentials" / "default.json" creds_path.parent.mkdir(parents=True, exist_ok=True) creds_path.write_text('"rt-SECRET"') # JSON scalar, not an object creds_path.chmod(0o600) with pytest.raises(AnthropicError, match="must contain a JSON object, not str") as exc_info: CredentialsFile()() _assert_not_in_sdk_frame_locals(exc_info.value, "rt-SECRET") @pytest.mark.respx(base_url=BASE_URL) def test_refresh_success_writes_raw_tokens_to_disk_and_wire( self, respx_mock: MockRouter, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path ) -> None: """SecretStr wrapping is an in-memory concern only: the refresh POST body and the persisted credentials file must carry the raw values — including unknown string fields, which are wrapped by default and must round-trip unchanged.""" clean_env.setattr("anthropic.lib.credentials._constants._config_dir", lambda: tmp_path) _write_profile( tmp_path, "default", config={"type": "authorized_user", "client_id": "cid"}, credentials={ "access_token": "old-access-SECRET", "expires_at": int(time.time()) - 1, "refresh_token": "rt-SECRET", "id_token": "idt-SECRET", }, ) respx_mock.post(TOKEN_ENDPOINT).mock( return_value=httpx.Response( 200, json={"access_token": "new-access", "expires_in": 3600, "refresh_token": "rt-NEW"} ) ) tok = CredentialsFile()() assert tok.token == "new-access" sent = json.loads(cast("List[MockRequestCall]", respx_mock.calls)[-1].request.content) assert sent == {"grant_type": "refresh_token", "refresh_token": "rt-SECRET", "client_id": "cid"} on_disk = json.loads((tmp_path / "credentials" / "default.json").read_text()) assert on_disk["access_token"] == "new-access" assert on_disk["refresh_token"] == "rt-NEW" assert on_disk["id_token"] == "idt-SECRET" def test_missing_access_token_does_not_retain_refresh_token( self, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path ) -> None: """A credentials file that has a refresh_token but no access_token raises before any exchange; the on-disk dict must not survive raw in frame locals.""" clean_env.setattr("anthropic.lib.credentials._constants._config_dir", lambda: tmp_path) _write_profile( tmp_path, "default", config={"type": "authorized_user", "client_id": "cid"}, credentials={"refresh_token": "rt-SECRET"}, ) with pytest.raises(AnthropicError, match="missing 'access_token'") as exc_info: CredentialsFile()() _assert_not_in_sdk_frame_locals(exc_info.value, "rt-SECRET") def test_invalid_json_credentials_does_not_retain_raw_text( self, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path ) -> None: clean_env.setattr("anthropic.lib.credentials._constants._config_dir", lambda: tmp_path) _write_profile(tmp_path, "default", config={"type": "authorized_user", "client_id": "cid"}) creds_path = tmp_path / "credentials" / "default.json" creds_path.parent.mkdir(parents=True, exist_ok=True) creds_path.write_text('{"refresh_token": "rt-SECRET", ') # truncated JSON creds_path.chmod(0o600) with pytest.raises(AnthropicError, match="not valid JSON") as exc_info: CredentialsFile()() _assert_not_in_sdk_frame_locals(exc_info.value, "rt-SECRET") # The json decoder frames hold the raw file text; the raise site must # have dropped the chained cause's traceback entirely. cause = exc_info.value.__cause__ assert cause is not None and cause.__traceback__ is None def test_credentials_type_mismatch_does_not_retain_tokens( self, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path ) -> None: clean_env.setattr("anthropic.lib.credentials._constants._config_dir", lambda: tmp_path) _write_profile( tmp_path, "default", config={"type": "authorized_user", "client_id": "cid"}, credentials={ "type": "wrong_type", "access_token": "at-SECRET", "refresh_token": "rt-SECRET", }, ) with pytest.raises(AnthropicError, match="has type") as exc_info: CredentialsFile()() _assert_not_in_sdk_frame_locals(exc_info.value, "at-SECRET", "rt-SECRET") def test_corrupt_expires_at_does_not_retain_tokens( self, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path ) -> None: """``_coerce_expires_at`` raises in a secret-free frame, but the error propagates through ``_call_user_oauth`` whose locals hold the creds dict — those locals must render redacted. The ``id_token`` field pins the secret-by-default rule: string fields the SDK doesn't know about are wrapped too.""" clean_env.setattr("anthropic.lib.credentials._constants._config_dir", lambda: tmp_path) _write_profile( tmp_path, "default", config={"type": "authorized_user", "client_id": "cid"}, credentials={ "access_token": "at-SECRET", "expires_at": "tomorrow", "refresh_token": "rt-SECRET", "id_token": "idt-SECRET", }, ) with pytest.raises(AnthropicError, match="invalid 'expires_at'") as exc_info: CredentialsFile()() _assert_not_in_sdk_frame_locals(exc_info.value, "at-SECRET", "rt-SECRET", "idt-SECRET") def test_corrupt_expires_at_external_profile_does_not_retain_token( self, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path ) -> None: """Same as above via the no-client_id (externally rotated) path.""" clean_env.setattr("anthropic.lib.credentials._constants._config_dir", lambda: tmp_path) _write_profile( tmp_path, "default", config={"type": "external"}, credentials={"access_token": "at-SECRET", "expires_at": "tomorrow"}, ) with pytest.raises(AnthropicError, match="invalid 'expires_at'") as exc_info: CredentialsFile()() _assert_not_in_sdk_frame_locals(exc_info.value, "at-SECRET") @pytest.mark.respx(base_url=BASE_URL) def test_failed_writeback_after_refresh_does_not_retain_tokens( self, respx_mock: MockRouter, clean_env: pytest.MonkeyPatch, tmp_path: pathlib.Path ) -> None: """A refresh that succeeds but fails to persist raises the write error (the refresh token may have rotated server-side) — but the propagating traceback's frames must not retain the new access token, the new or old refresh token, or the creds dict.""" self._write_refresh_profile(clean_env, tmp_path) respx_mock.post(TOKEN_ENDPOINT).mock( return_value=httpx.Response( 200, json={"access_token": _SECRET_MINTED, "expires_in": 3600, "refresh_token": "rt-NEW-SECRET"} ) ) # Make persistence fail the way a full disk would: mkstemp raises. def _mkstemp_enospc(*_args: Any, **_kwargs: Any) -> "tuple[int, str]": raise OSError(28, "No space left on device") clean_env.setattr("anthropic.lib.credentials._providers.tempfile.mkstemp", _mkstemp_enospc) with pytest.raises(OSError, match="No space left") as exc_info: CredentialsFile()() _assert_not_in_sdk_frame_locals( exc_info.value, "rt-SECRET", "old-access-SECRET", _SECRET_MINTED, "rt-NEW-SECRET" ) def test_oneshot_exchange_failure_does_not_retain_assertion(self) -> None: """``exchange_federation_assertion`` holds the caller's assertion as a parameter in its own (SDK-owned) frame; it is rebound to SecretStr on entry so a failed exchange renders it redacted. The caller's own frame is beyond the SDK's reach.""" with pytest.raises(WorkloadIdentityError) as exc_info: exchange_federation_assertion( assertion=_SECRET_ASSERTION, federation_rule_id="fdrl_01abc", organization_id="org-uuid", base_url=BASE_URL, http_client=httpx.Client( transport=httpx.MockTransport(lambda _: httpx.Response(401, json={"error": "invalid_grant"})) ), ) _assert_not_in_sdk_frame_locals(exc_info.value, _SECRET_ASSERTION) anthropic-sdk-python-0.120.2/tests/lib/test_google_cloud.py000066400000000000000000001413071523216435200237350ustar00rootroot00000000000000from __future__ import annotations import re import sys import json import time import logging import pathlib import threading from typing import Any, cast import httpx import pytest from respx import MockRouter from anthropic._exceptions import AnthropicError from anthropic.lib.google_cloud import AnthropicGoogleCloud, AsyncAnthropicGoogleCloud, _client as google_cloud_module from anthropic.lib._extras._common import MissingDependencyError # httpx normalizes the client base URL with a trailing slash. DERIVED_BASE_URL = ( "https://claude.googleapis.com/v1alpha/projects/my-project/locations/us-central1/workspaces/wrkspc_x/invoke/" ) GLOBAL_DERIVED_BASE_URL = ( "https://claude.googleapis.com/v1alpha/projects/my-project/locations/global/workspaces/wrkspc_x/invoke/" ) @pytest.fixture(autouse=True) def _isolate_environment(monkeypatch: pytest.MonkeyPatch) -> None: # pyright: ignore[reportUnusedFunction] """Ambient first-party / google-cloud env vars must not leak into these tests.""" for name in ( "ANTHROPIC_GOOGLE_CLOUD_PROJECT", "ANTHROPIC_GOOGLE_CLOUD_LOCATION", "ANTHROPIC_GOOGLE_CLOUD_WORKSPACE_ID", "ANTHROPIC_GOOGLE_CLOUD_BASE_URL", "GOOGLE_CLOUD_PROJECT", "ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN", "ANTHROPIC_BASE_URL", "ANTHROPIC_CUSTOM_HEADERS", "ANTHROPIC_WEBHOOK_SIGNING_KEY", "ANTHROPIC_CONFIG_DIR", "ANTHROPIC_PROFILE", ): monkeypatch.delenv(name, raising=False) class _FakeCredentials: """Duck-typed stand-in for a `google.auth` Credentials object.""" def __init__(self, token: str | None, *, expired: bool = False, project_id: str | None = None) -> None: self.token = token self.expired = expired self.project_id = project_id self.refresh_calls = 0 def refresh(self, _request: object) -> None: self.refresh_calls += 1 self.token = "refreshed-token" self.expired = False # --------------------------------------------------------------------------- # Initialization / base URL / workspace # --------------------------------------------------------------------------- class TestAnthropicGoogleCloud: def test_init_with_token_provider_and_base_url(self) -> None: client = AnthropicGoogleCloud( base_url="https://example.test/", workspace_id="wrkspc_x", token_provider=lambda: "tok", ) assert str(client.base_url) == "https://example.test/" assert client.workspace_id == "wrkspc_x" def test_explicit_base_url_needs_no_location(self) -> None: # No project / location required when base_url is explicit. client = AnthropicGoogleCloud(base_url="https://example.test/", workspace_id="wrkspc_x") assert str(client.base_url) == "https://example.test/" def test_base_url_derived_from_project_and_location(self) -> None: client = AnthropicGoogleCloud( project="my-project", location="us-central1", workspace_id="wrkspc_x", token_provider=lambda: "tok", ) assert str(client.base_url) == DERIVED_BASE_URL def test_location_defaults_to_global(self) -> None: client = AnthropicGoogleCloud(project="my-project", workspace_id="wrkspc_x", token_provider=lambda: "tok") assert str(client.base_url) == GLOBAL_DERIVED_BASE_URL def test_missing_project_defers_without_construct_error(self) -> None: # The project is back-filled from Google credentials on the first request. AnthropicGoogleCloud(location="us-central1", workspace_id="wrkspc_x") def test_skip_auth_deriving_base_url_requires_project(self) -> None: # With skip_auth there are no Google credentials to back-fill the project from. with pytest.raises(ValueError, match="project"): AnthropicGoogleCloud(location="us-central1", workspace_id="wrkspc_x", skip_auth=True) def test_skip_auth_deriving_base_url_requires_workspace(self) -> None: # The workspace ID is part of the derived URL, so skip_auth alone no longer # waives it — only an explicit base_url does. with pytest.raises(ValueError, match=r"(?s)workspace_id.*ANTHROPIC_GOOGLE_CLOUD_WORKSPACE_ID"): AnthropicGoogleCloud(project="my-project", skip_auth=True) def test_skip_auth_derives_url_with_workspace(self) -> None: client = AnthropicGoogleCloud(project="my-project", workspace_id="wrkspc_x", skip_auth=True) assert str(client.base_url) == GLOBAL_DERIVED_BASE_URL def test_env_resolution(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_GOOGLE_CLOUD_BASE_URL", "https://env.test/") monkeypatch.setenv("ANTHROPIC_GOOGLE_CLOUD_WORKSPACE_ID", "wrkspc_env") client = AnthropicGoogleCloud(token_provider=lambda: "tok") assert str(client.base_url) == "https://env.test/" assert client.workspace_id == "wrkspc_env" def test_explicit_project_arg_beats_env(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_GOOGLE_CLOUD_PROJECT", "env-project") client = AnthropicGoogleCloud( project="arg-project", location="us-central1", workspace_id="wrkspc_x", token_provider=lambda: "tok" ) assert "/projects/arg-project/" in str(client.base_url) assert "env-project" not in str(client.base_url) def test_location_from_env(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_GOOGLE_CLOUD_LOCATION", "europe-west4") client = AnthropicGoogleCloud(project="my-project", workspace_id="wrkspc_x", token_provider=lambda: "tok") assert str(client.base_url).startswith("https://claude.googleapis.com/") assert "/locations/europe-west4/" in str(client.base_url) def test_explicit_location_arg_beats_env(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_GOOGLE_CLOUD_LOCATION", "env-location") client = AnthropicGoogleCloud( project="my-project", location="us-central1", workspace_id="wrkspc_x", token_provider=lambda: "tok" ) assert str(client.base_url) == DERIVED_BASE_URL def test_project_falls_back_to_google_cloud_project_env(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("GOOGLE_CLOUD_PROJECT", "gcp-env-project") client = AnthropicGoogleCloud(location="us-central1", workspace_id="wrkspc_x", token_provider=lambda: "tok") assert "/projects/gcp-env-project/" in str(client.base_url) def test_anthropic_project_env_beats_google_cloud_project(self, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_GOOGLE_CLOUD_PROJECT", "anthropic-env-project") monkeypatch.setenv("GOOGLE_CLOUD_PROJECT", "gcp-env-project") client = AnthropicGoogleCloud(location="us-central1", workspace_id="wrkspc_x", token_provider=lambda: "tok") assert "/projects/anthropic-env-project/" in str(client.base_url) assert "gcp-env-project" not in str(client.base_url) def test_workspace_required(self) -> None: with pytest.raises(ValueError, match="workspace ID"): AnthropicGoogleCloud(base_url="https://example.test/", token_provider=lambda: "tok") def test_api_key_and_auth_token_rejected(self) -> None: with pytest.raises(TypeError): AnthropicGoogleCloud(base_url="https://example.test/", workspace_id="wrkspc_x", api_key="sk-ant-x") # type: ignore[call-arg] with pytest.raises(TypeError): AnthropicGoogleCloud(base_url="https://example.test/", workspace_id="wrkspc_x", auth_token="tok") # type: ignore[call-arg] def test_skip_auth_mutually_exclusive_with_credentials(self) -> None: with pytest.raises(ValueError, match="mutually exclusive"): AnthropicGoogleCloud(base_url="https://example.test/", skip_auth=True, token_provider=lambda: "tok") with pytest.raises(ValueError, match="mutually exclusive"): AnthropicGoogleCloud( base_url="https://example.test/", skip_auth=True, credentials=cast(Any, _FakeCredentials(token="tok")), ) def test_project_backfilled_from_explicit_credentials(self) -> None: # Service-account-style credentials expose their project; the base URL can # then be derived at construction without an explicit `project`. creds = _FakeCredentials(token="tok", project_id="creds-project") client = AnthropicGoogleCloud(location="us-central1", workspace_id="wrkspc_x", credentials=cast(Any, creds)) assert "/projects/creds-project/" in str(client.base_url) def test_google_credentials_exposed_without_shadowing_base_attribute(self) -> None: creds = _FakeCredentials(token="tok") client = AnthropicGoogleCloud( base_url="https://example.test/", workspace_id="wrkspc_x", credentials=cast(Any, creds) ) assert client.google_credentials is creds # `.credentials` is the base client's first-party provider slot — never the # Google credentials, and never engaged on this client. assert client.credentials is None def test_completions_is_none(self) -> None: client = AnthropicGoogleCloud( base_url="https://example.test/", workspace_id="wrkspc_x", token_provider=lambda: "tok" ) assert client.completions is None def test_full_surface_present(self) -> None: client = AnthropicGoogleCloud( base_url="https://example.test/", workspace_id="wrkspc_x", token_provider=lambda: "tok" ) assert client.messages is not None assert client.models is not None assert client.beta is not None assert client.messages.batches is not None def test_no_spurious_env_shadow_warning(self, monkeypatch: pytest.MonkeyPatch) -> None: # The base client's "unset ANTHROPIC_API_KEY" credential-precedence warning # is about its auto-discovery chain, which never engages for this subclass. calls: list[Any] = [] def _record(**kwargs: object) -> None: calls.append(kwargs) monkeypatch.setattr("anthropic._client._warn_env_shadow", _record) monkeypatch.setenv("ANTHROPIC_API_KEY", "sk-ant-x") AnthropicGoogleCloud(base_url="https://example.test/", workspace_id="wrkspc_x", token_provider=lambda: "tok") assert calls == [] # --------------------------------------------------------------------------- # Auth attachment # --------------------------------------------------------------------------- @pytest.mark.respx() def test_token_provider_attaches_bearer(respx_mock: MockRouter) -> None: respx_mock.post(re.compile(r"https://example\.test/.*")).mock(return_value=httpx.Response(200, json={"foo": "bar"})) client = AnthropicGoogleCloud( base_url="https://example.test/", workspace_id="wrkspc_x", token_provider=lambda: "provided-token", ) client.messages.create(max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5") calls = cast("list[Any]", respx_mock.calls) assert len(calls) == 1 req = calls[0].request assert req.headers["Authorization"] == "Bearer provided-token" # The workspace id travels in the URL path only — never as a header. assert "anthropic-workspace-id" not in req.headers @pytest.mark.respx() async def test_token_provider_attaches_bearer_async(respx_mock: MockRouter) -> None: respx_mock.post(re.compile(r"https://example\.test/.*")).mock(return_value=httpx.Response(200, json={"foo": "bar"})) async def provider() -> str: return "async-token" client = AsyncAnthropicGoogleCloud( base_url="https://example.test/", workspace_id="wrkspc_x", token_provider=provider, ) await client.messages.create(max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5") calls = cast("list[Any]", respx_mock.calls) assert len(calls) == 1 assert calls[0].request.headers["Authorization"] == "Bearer async-token" assert "anthropic-workspace-id" not in calls[0].request.headers @pytest.mark.respx() async def test_sync_token_provider_on_async_client_runs_off_event_loop( respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch ) -> None: respx_mock.post(re.compile(r"https://example\.test/.*")).mock(return_value=httpx.Response(200, json={"foo": "bar"})) offloaded: list[Any] = [] real_asyncify = google_cloud_module.asyncify def spy(fn: Any) -> Any: offloaded.append(fn) return real_asyncify(fn) monkeypatch.setattr("anthropic.lib.google_cloud._client.asyncify", spy) def provider() -> str: return "sync-token" client = AsyncAnthropicGoogleCloud( base_url="https://example.test/", workspace_id="wrkspc_x", token_provider=provider ) await client.messages.create(max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5") calls = cast("list[Any]", respx_mock.calls) assert calls[0].request.headers["Authorization"] == "Bearer sync-token" assert provider in offloaded # sync providers may block — they must not run inline on the loop def test_async_token_provider_on_sync_client_rejected_clearly() -> None: async def provider() -> str: return "tok" client = AnthropicGoogleCloud( base_url="https://example.test/", workspace_id="wrkspc_x", token_provider=cast(Any, provider) ) # Also implicitly asserts no "coroutine was never awaited" RuntimeWarning # escapes (filterwarnings=error would fail the test). with pytest.raises(AnthropicError, match="AsyncAnthropicGoogleCloud"): client.messages.create(max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5") @pytest.mark.respx() def test_credentials_object_attaches_bearer(respx_mock: MockRouter) -> None: respx_mock.post(re.compile(r"https://example\.test/.*")).mock(return_value=httpx.Response(200, json={"foo": "bar"})) creds = _FakeCredentials(token="cred-token") client = AnthropicGoogleCloud( base_url="https://example.test/", workspace_id="wrkspc_x", credentials=cast(Any, creds), ) client.messages.create(max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5") calls = cast("list[Any]", respx_mock.calls) assert calls[0].request.headers["Authorization"] == "Bearer cred-token" assert creds.refresh_calls == 0 # fresh creds aren't refreshed @pytest.mark.respx() def test_expired_credentials_refreshed(respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: respx_mock.post(re.compile(r"https://example\.test/.*")).mock(return_value=httpx.Response(200, json={"foo": "bar"})) # Avoid the real google-auth Request import (optional dep, not installed here). def _fake_refresh(creds: _FakeCredentials) -> None: creds.refresh(None) monkeypatch.setattr("anthropic.lib.google_cloud._client._refresh_credentials", _fake_refresh) creds = _FakeCredentials(token="stale", expired=True) client = AnthropicGoogleCloud( base_url="https://example.test/", workspace_id="wrkspc_x", credentials=cast(Any, creds), ) client.messages.create(max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5") calls = cast("list[Any]", respx_mock.calls) assert creds.refresh_calls == 1 assert calls[0].request.headers["Authorization"] == "Bearer refreshed-token" def test_refresh_without_google_auth_raises_actionable_error(monkeypatch: pytest.MonkeyPatch) -> None: # `None` in sys.modules makes the import fail even when google-auth is installed. monkeypatch.setitem(sys.modules, "google.auth.transport.requests", cast(Any, None)) with pytest.raises(MissingDependencyError, match=r"anthropic\[google_cloud\]"): google_cloud_module._refresh_credentials(cast(Any, _FakeCredentials(token=None))) @pytest.mark.respx() def test_adc_path(respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: respx_mock.post(re.compile(r"https://example\.test/.*")).mock(return_value=httpx.Response(200, json={"foo": "bar"})) monkeypatch.setattr( "anthropic.lib.google_cloud._client._load_adc_credentials", lambda: (_FakeCredentials(token="adc-token"), None), ) client = AnthropicGoogleCloud(base_url="https://example.test/", workspace_id="wrkspc_x") client.messages.create(max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5") calls = cast("list[Any]", respx_mock.calls) assert calls[0].request.headers["Authorization"] == "Bearer adc-token" @pytest.mark.respx() @pytest.mark.parametrize( "kwarg", [ {"token_provider": lambda: "tok"}, {"credentials": _FakeCredentials(token="tok")}, ], ids=["token_provider", "credentials"], ) def test_explicit_credential_suppresses_adc( respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch, kwarg: dict[str, Any] ) -> None: # With any explicit credential source set, ADC discovery must never run. respx_mock.post(re.compile(r"https://example\.test/.*")).mock(return_value=httpx.Response(200, json={"foo": "bar"})) def _fail() -> Any: raise AssertionError("ADC discovery must not run when an explicit credential source is set") monkeypatch.setattr("anthropic.lib.google_cloud._client._load_adc_credentials", _fail) client = AnthropicGoogleCloud(base_url="https://example.test/", workspace_id="wrkspc_x", **kwarg) client.messages.create(max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5") calls = cast("list[Any]", respx_mock.calls) assert calls[0].request.headers["Authorization"] == "Bearer tok" @pytest.mark.respx() async def test_explicit_credential_suppresses_adc_async( respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch ) -> None: respx_mock.post(re.compile(r"https://example\.test/.*")).mock(return_value=httpx.Response(200, json={"foo": "bar"})) def _fail() -> Any: raise AssertionError("ADC discovery must not run when an explicit credential source is set") monkeypatch.setattr("anthropic.lib.google_cloud._client._load_adc_credentials", _fail) client = AsyncAnthropicGoogleCloud( base_url="https://example.test/", workspace_id="wrkspc_x", token_provider=lambda: "tok" ) await client.messages.create(max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5") calls = cast("list[Any]", respx_mock.calls) assert calls[0].request.headers["Authorization"] == "Bearer tok" def test_concurrent_first_token_resolution_loads_adc_once(monkeypatch: pytest.MonkeyPatch) -> None: load_calls = 0 def slow_load() -> Any: nonlocal load_calls load_calls += 1 time.sleep(0.05) return _FakeCredentials(token="adc-token"), None monkeypatch.setattr("anthropic.lib.google_cloud._client._load_adc_credentials", slow_load) client = AnthropicGoogleCloud(base_url="https://example.test/", workspace_id="wrkspc_x") tokens: list[str] = [] threads = [threading.Thread(target=lambda: tokens.append(client._get_token())) for _ in range(4)] for thread in threads: thread.start() for thread in threads: thread.join() assert tokens == ["adc-token"] * 4 assert load_calls == 1 # the lazy load is serialized, not raced @pytest.mark.respx() def test_bearer_token_not_in_debug_logs(respx_mock: MockRouter, caplog: pytest.LogCaptureFixture) -> None: respx_mock.post(re.compile(r"https://example\.test/.*")).mock(return_value=httpx.Response(200, json={"foo": "bar"})) caplog.set_level(logging.DEBUG) client = AnthropicGoogleCloud( base_url="https://example.test/", workspace_id="wrkspc_x", token_provider=lambda: "super-secret-gcp-token" ) client.messages.create(max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5") calls = cast("list[Any]", respx_mock.calls) assert calls[0].request.headers["Authorization"] == "Bearer super-secret-gcp-token" assert "super-secret-gcp-token" not in caplog.text # --------------------------------------------------------------------------- # Caller-supplied headers win, exactly once, regardless of case / source # --------------------------------------------------------------------------- @pytest.mark.respx() def test_lowercase_authorization_header_respected(respx_mock: MockRouter) -> None: respx_mock.post(re.compile(r"https://example\.test/.*")).mock(return_value=httpx.Response(200, json={"foo": "bar"})) client = AnthropicGoogleCloud( base_url="https://example.test/", workspace_id="wrkspc_x", token_provider=lambda: "gcp-token" ) client.messages.create( max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5", extra_headers={"authorization": "Bearer caller-token"}, ) calls = cast("list[Any]", respx_mock.calls) assert calls[0].request.headers.get_list("authorization") == ["Bearer caller-token"] @pytest.mark.respx() def test_custom_headers_env_flows_through(respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: # ANTHROPIC_CUSTOM_HEADERS behaves like it does on other SDK clients. monkeypatch.setenv("ANTHROPIC_CUSTOM_HEADERS", "x-custom-header: hello") respx_mock.post(re.compile(r"https://example\.test/.*")).mock(return_value=httpx.Response(200, json={"foo": "bar"})) client = AnthropicGoogleCloud( base_url="https://example.test/", workspace_id="wrkspc_x", token_provider=lambda: "gcp-token" ) client.messages.create(max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5") calls = cast("list[Any]", respx_mock.calls) req = calls[0].request assert req.headers["x-custom-header"] == "hello" assert req.headers["Authorization"] == "Bearer gcp-token" @pytest.mark.respx() def test_custom_headers_env_authorization_never_conflicts( respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch ) -> None: # An env-supplied Authorization wins under the only-if-absent contract — and # exactly one Authorization header goes out, never two conflicting ones. monkeypatch.setenv("ANTHROPIC_CUSTOM_HEADERS", "Authorization: Bearer env-token") respx_mock.post(re.compile(r"https://example\.test/.*")).mock(return_value=httpx.Response(200, json={"foo": "bar"})) client = AnthropicGoogleCloud( base_url="https://example.test/", workspace_id="wrkspc_x", token_provider=lambda: "gcp-token" ) client.messages.create(max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5") calls = cast("list[Any]", respx_mock.calls) assert calls[0].request.headers.get_list("authorization") == ["Bearer env-token"] # --------------------------------------------------------------------------- # Lazy project back-fill: base URL derived on the first request from the project # that ADC resolves # --------------------------------------------------------------------------- ADC_DERIVED_BASE_URL = ( "https://claude.googleapis.com/v1alpha/projects/adc-project/locations/us-central1/workspaces/wrkspc_x/invoke/" ) @pytest.mark.respx() def test_project_backfilled_from_adc_on_first_request(respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: respx_mock.post(re.compile(r"https://claude\.googleapis\.com/.*")).mock( return_value=httpx.Response(200, json={"foo": "bar"}) ) load_calls = 0 def fake_load() -> Any: nonlocal load_calls load_calls += 1 return _FakeCredentials(token="adc-token"), "adc-project" monkeypatch.setattr("anthropic.lib.google_cloud._client._load_adc_credentials", fake_load) client = AnthropicGoogleCloud(location="us-central1", workspace_id="wrkspc_x") client.messages.create(max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5") client.messages.create(max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5") calls = cast("list[Any]", respx_mock.calls) assert len(calls) == 2 for call in calls: assert str(call.request.url).startswith(ADC_DERIVED_BASE_URL.rstrip("/")) assert call.request.headers["Authorization"] == "Bearer adc-token" # The derived URL carries the workspace in its path; still no header. assert "anthropic-workspace-id" not in call.request.headers assert str(client.base_url) == ADC_DERIVED_BASE_URL assert load_calls == 1 # the ADC load (and the back-fill) happens once @pytest.mark.respx() def test_backfill_happens_even_with_caller_authorization( respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch ) -> None: # The back-fill is decoupled from token attachment: a first request carrying # its own Authorization header must still resolve the deferred base URL. respx_mock.post(re.compile(r"https://claude\.googleapis\.com/.*")).mock( return_value=httpx.Response(200, json={"foo": "bar"}) ) monkeypatch.setattr( "anthropic.lib.google_cloud._client._load_adc_credentials", lambda: (_FakeCredentials(token="adc-token"), "adc-project"), ) client = AnthropicGoogleCloud(location="us-central1", workspace_id="wrkspc_x") client.messages.create( max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5", extra_headers={"Authorization": "Bearer caller-token"}, ) calls = cast("list[Any]", respx_mock.calls) req = calls[0].request assert str(req.url).startswith(ADC_DERIVED_BASE_URL.rstrip("/")) assert req.headers.get_list("authorization") == ["Bearer caller-token"] def test_adc_without_project_raises_on_first_request(monkeypatch: pytest.MonkeyPatch) -> None: # Plain user ADC resolves no project; the error surfaces on the first request. monkeypatch.setattr( "anthropic.lib.google_cloud._client._load_adc_credentials", lambda: (_FakeCredentials(token="adc-token"), None), ) client = AnthropicGoogleCloud(location="us-central1", workspace_id="wrkspc_x") with pytest.raises(AnthropicError, match="ANTHROPIC_GOOGLE_CLOUD_PROJECT"): client.messages.create(max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5") def test_token_provider_without_project_raises_on_first_request() -> None: # A token provider bypasses ADC entirely, so there is nothing to back-fill from. client = AnthropicGoogleCloud(location="us-central1", workspace_id="wrkspc_x", token_provider=lambda: "tok") with pytest.raises(AnthropicError, match="project"): client.messages.create(max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5") @pytest.mark.respx() def test_post_construction_base_url_assignment_wins(respx_mock: MockRouter) -> None: # Assigning base_url on a deferred-project client cancels the pending back-fill # instead of being clobbered (or raising a spurious missing-project error). respx_mock.post(re.compile(r"https://my-gateway\.test/.*")).mock( return_value=httpx.Response(200, json={"foo": "bar"}) ) client = AnthropicGoogleCloud(location="us-central1", workspace_id="wrkspc_x", token_provider=lambda: "tok") client.base_url = "https://my-gateway.test/" client.messages.create(max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5") calls = cast("list[Any]", respx_mock.calls) req = calls[0].request assert req.url.host == "my-gateway.test" assert req.headers["Authorization"] == "Bearer tok" @pytest.mark.respx() def test_copy_of_deferred_client_still_backfills_and_shares_adc( respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch ) -> None: respx_mock.post(re.compile(r"https://claude\.googleapis\.com/.*")).mock( return_value=httpx.Response(200, json={"foo": "bar"}) ) load_calls = 0 def fake_load() -> Any: nonlocal load_calls load_calls += 1 return _FakeCredentials(token="adc-token"), "adc-project" monkeypatch.setattr("anthropic.lib.google_cloud._client._load_adc_credentials", fake_load) client = AnthropicGoogleCloud(location="us-central1", workspace_id="wrkspc_x") clone = client.with_options(timeout=10) clone.messages.create(max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5") client.messages.create(max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5") calls = cast("list[Any]", respx_mock.calls) assert len(calls) == 2 for call in calls: assert str(call.request.url).startswith(ADC_DERIVED_BASE_URL.rstrip("/")) assert load_calls == 1 # clones share the lazily-loaded ADC credentials @pytest.mark.respx() async def test_project_backfilled_from_adc_on_first_request_async( respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch ) -> None: respx_mock.post(re.compile(r"https://claude\.googleapis\.com/.*")).mock( return_value=httpx.Response(200, json={"foo": "bar"}) ) monkeypatch.setattr( "anthropic.lib.google_cloud._client._load_adc_credentials", lambda: (_FakeCredentials(token="adc-token"), "adc-project"), ) client = AsyncAnthropicGoogleCloud(location="us-central1", workspace_id="wrkspc_x") await client.messages.create(max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5") calls = cast("list[Any]", respx_mock.calls) assert str(calls[0].request.url).startswith(ADC_DERIVED_BASE_URL.rstrip("/")) assert "anthropic-workspace-id" not in calls[0].request.headers assert str(client.base_url) == ADC_DERIVED_BASE_URL async def test_adc_without_project_raises_on_first_request_async(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr( "anthropic.lib.google_cloud._client._load_adc_credentials", lambda: (_FakeCredentials(token="adc-token"), None), ) client = AsyncAnthropicGoogleCloud(location="us-central1", workspace_id="wrkspc_x") with pytest.raises(AnthropicError, match="ANTHROPIC_GOOGLE_CLOUD_PROJECT"): await client.messages.create( max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5" ) # --------------------------------------------------------------------------- # copy() / with_options() coherence # --------------------------------------------------------------------------- class TestCopy: def test_copy_project_rederives_base_url(self) -> None: client = AnthropicGoogleCloud( project="my-project", location="us-central1", workspace_id="wrkspc_x", token_provider=lambda: "tok" ) clone = client.copy(project="other-project") assert "/projects/other-project/" in str(clone.base_url) def test_copy_location_rederives_base_url(self) -> None: client = AnthropicGoogleCloud( project="my-project", location="us-central1", workspace_id="wrkspc_x", token_provider=lambda: "tok" ) clone = client.copy(location="europe-west4") assert str(clone.base_url).startswith("https://claude.googleapis.com/") assert "/locations/europe-west4/" in str(clone.base_url) def test_copy_keeps_user_supplied_base_url(self) -> None: client = AnthropicGoogleCloud( base_url="https://example.test/", workspace_id="wrkspc_x", token_provider=lambda: "tok" ) clone = client.copy(project="other-project") assert str(clone.base_url) == "https://example.test/" def test_copy_plain_roundtrip_keeps_derived_base_url(self) -> None: client = AnthropicGoogleCloud( project="my-project", location="us-central1", workspace_id="wrkspc_x", token_provider=lambda: "tok" ) clone = client.copy() assert str(clone.base_url) == DERIVED_BASE_URL @pytest.mark.respx() def test_copy_lower_tier_credential_takes_effect(self, respx_mock: MockRouter) -> None: # copy(credentials=...) on a token_provider client must not keep calling # the inherited (higher-precedence) provider. respx_mock.post(re.compile(r"https://example\.test/.*")).mock( return_value=httpx.Response(200, json={"foo": "bar"}) ) def provider() -> str: raise AssertionError("inherited token_provider must not be used after copy(credentials=...)") client = AnthropicGoogleCloud( base_url="https://example.test/", workspace_id="wrkspc_x", token_provider=provider ) clone = client.copy(credentials=cast(Any, _FakeCredentials(token="cred-token"))) clone.messages.create(max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5") calls = cast("list[Any]", respx_mock.calls) assert calls[0].request.headers["Authorization"] == "Bearer cred-token" @pytest.mark.respx() def test_copy_token_provider_replaces_credentials(self, respx_mock: MockRouter) -> None: respx_mock.post(re.compile(r"https://example\.test/.*")).mock( return_value=httpx.Response(200, json={"foo": "bar"}) ) client = AnthropicGoogleCloud( base_url="https://example.test/", workspace_id="wrkspc_x", credentials=cast(Any, _FakeCredentials(token="stale")), ) clone = client.copy(token_provider=lambda: "fresh-token") clone.messages.create(max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5") calls = cast("list[Any]", respx_mock.calls) assert calls[0].request.headers["Authorization"] == "Bearer fresh-token" @pytest.mark.respx() def test_copy_skip_auth_clears_workspace_and_credentials(self, respx_mock: MockRouter) -> None: # The documented pre-authenticated-proxy derivation: no bearer, and the # workspace ID is clearable on the clone. respx_mock.post(re.compile(r"https://proxy\.test/.*")).mock( return_value=httpx.Response(200, json={"foo": "bar"}) ) client = AnthropicGoogleCloud( base_url="https://example.test/", workspace_id="wrkspc_x", token_provider=lambda: "tok" ) proxy = client.copy(skip_auth=True, workspace_id=None, base_url="https://proxy.test/") proxy.messages.create(max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5") calls = cast("list[Any]", respx_mock.calls) req = calls[0].request assert "Authorization" not in req.headers assert "anthropic-workspace-id" not in req.headers def test_copy_workspace_required_when_cleared_without_skip_auth(self) -> None: client = AnthropicGoogleCloud( base_url="https://example.test/", workspace_id="wrkspc_x", token_provider=lambda: "tok" ) with pytest.raises(ValueError, match="workspace ID"): client.copy(workspace_id=None) def test_copy_rejects_first_party_kwargs(self) -> None: client = AnthropicGoogleCloud( base_url="https://example.test/", workspace_id="wrkspc_x", token_provider=lambda: "tok" ) with pytest.raises(TypeError): client.copy(auth_token="environment-key") # type: ignore[call-arg] with pytest.raises(TypeError): client.copy(api_key="sk-ant-x") # type: ignore[call-arg] def test_async_copy_parity(self) -> None: client = AsyncAnthropicGoogleCloud( project="my-project", location="us-central1", workspace_id="wrkspc_x", token_provider=lambda: "tok" ) clone = client.copy(project="other-project") assert "/projects/other-project/" in str(clone.base_url) proxy = client.copy(skip_auth=True, workspace_id=None, base_url="https://proxy.test/") assert proxy.workspace_id is None with pytest.raises(TypeError): client.copy(auth_token="environment-key") # type: ignore[call-arg] # --------------------------------------------------------------------------- # skip_auth # --------------------------------------------------------------------------- @pytest.mark.respx() def test_skip_auth_sends_neither_header(respx_mock: MockRouter) -> None: respx_mock.post(re.compile(r"https://example\.test/.*")).mock(return_value=httpx.Response(200, json={"foo": "bar"})) client = AnthropicGoogleCloud(base_url="https://example.test/", skip_auth=True) assert client.workspace_id is None client.messages.create(max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5") calls = cast("list[Any]", respx_mock.calls) req = calls[0].request assert "Authorization" not in req.headers assert "anthropic-workspace-id" not in req.headers # --------------------------------------------------------------------------- # Credential isolation: the first-party ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN / # ANTHROPIC_BASE_URL environment variables and the base SDK credential-discovery # chain must never affect requests made by this client. # --------------------------------------------------------------------------- @pytest.mark.respx() def test_no_api_key_leak(respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_API_KEY", "sk-leak") respx_mock.post(re.compile(r"https://example\.test/.*")).mock(return_value=httpx.Response(200, json={"foo": "bar"})) client = AnthropicGoogleCloud( base_url="https://example.test/", workspace_id="wrkspc_x", token_provider=lambda: "tok" ) assert client.api_key is None assert client.auth_headers == {} client.messages.create(max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5") calls = cast("list[Any]", respx_mock.calls) req = calls[0].request assert "x-api-key" not in {k.lower() for k in req.headers.keys()} assert req.headers["Authorization"] == "Bearer tok" @pytest.mark.respx() async def test_no_api_key_leak_async(respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_API_KEY", "sk-leak") respx_mock.post(re.compile(r"https://example\.test/.*")).mock(return_value=httpx.Response(200, json={"foo": "bar"})) client = AsyncAnthropicGoogleCloud( base_url="https://example.test/", workspace_id="wrkspc_x", token_provider=lambda: "tok" ) assert client.api_key is None await client.messages.create(max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5") calls = cast("list[Any]", respx_mock.calls) req = calls[0].request assert "x-api-key" not in {k.lower() for k in req.headers.keys()} @pytest.mark.respx() def test_no_auth_token_leak(respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: # An ANTHROPIC_AUTH_TOKEN in the env must never become the Authorization header — # the bearer token sent is always the one this client resolved itself. monkeypatch.setenv("ANTHROPIC_AUTH_TOKEN", "first-party-leak") respx_mock.post(re.compile(r"https://example\.test/.*")).mock(return_value=httpx.Response(200, json={"foo": "bar"})) client = AnthropicGoogleCloud( base_url="https://example.test/", workspace_id="wrkspc_x", token_provider=lambda: "gcp-token" ) assert client.auth_token is None client.messages.create(max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5") calls = cast("list[Any]", respx_mock.calls) assert calls[0].request.headers["Authorization"] == "Bearer gcp-token" @pytest.mark.respx() async def test_no_auth_token_leak_async(respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_AUTH_TOKEN", "first-party-leak") respx_mock.post(re.compile(r"https://example\.test/.*")).mock(return_value=httpx.Response(200, json={"foo": "bar"})) client = AsyncAnthropicGoogleCloud( base_url="https://example.test/", workspace_id="wrkspc_x", token_provider=lambda: "gcp-token" ) assert client.auth_token is None await client.messages.create(max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5") calls = cast("list[Any]", respx_mock.calls) assert calls[0].request.headers["Authorization"] == "Bearer gcp-token" @pytest.mark.respx() def test_env_base_url_does_not_override_derived_url(respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch) -> None: # ANTHROPIC_BASE_URL configures the first-party client only; the gateway URL # derived from project/location always wins here. monkeypatch.setenv("ANTHROPIC_BASE_URL", "https://first-party.test/") respx_mock.post(re.compile(r"https://claude\.googleapis\.com/.*")).mock( return_value=httpx.Response(200, json={"foo": "bar"}) ) client = AnthropicGoogleCloud( project="my-project", location="us-central1", workspace_id="wrkspc_x", token_provider=lambda: "tok" ) assert str(client.base_url) == DERIVED_BASE_URL client.messages.create(max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5") calls = cast("list[Any]", respx_mock.calls) assert str(calls[0].request.url).startswith(DERIVED_BASE_URL.rstrip("/")) async def test_env_base_url_does_not_override_derived_url_async(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("ANTHROPIC_BASE_URL", "https://first-party.test/") client = AsyncAnthropicGoogleCloud( project="my-project", location="us-central1", workspace_id="wrkspc_x", token_provider=lambda: "tok" ) assert str(client.base_url) == DERIVED_BASE_URL def _assert_credential_chain_off(client: AnthropicGoogleCloud | AsyncAnthropicGoogleCloud) -> None: from anthropic._client import _is_base_client assert _is_base_client(client) is False assert client.credentials is None assert client.api_key is None assert client.auth_token is None def test_base_credential_chain_never_engages(monkeypatch: pytest.MonkeyPatch) -> None: # The base client's credential auto-discovery (profiles / workload identity / # token cache) is gated on `_is_base_client()`; pin that it never runs for # this subclass even when no other credential source is configured. def _fail(*_args: Any, **_kwargs: Any) -> Any: raise AssertionError("base credential-discovery chain must not engage for AnthropicGoogleCloud") monkeypatch.setattr("anthropic._client.default_credentials", _fail) client = AnthropicGoogleCloud( base_url="https://example.test/", workspace_id="wrkspc_x", token_provider=lambda: "tok" ) _assert_credential_chain_off(client) async def test_base_credential_chain_never_engages_async(monkeypatch: pytest.MonkeyPatch) -> None: def _fail(*_args: Any, **_kwargs: Any) -> Any: raise AssertionError("base credential-discovery chain must not engage for AsyncAnthropicGoogleCloud") monkeypatch.setattr("anthropic._client.default_credentials", _fail) client = AsyncAnthropicGoogleCloud( base_url="https://example.test/", workspace_id="wrkspc_x", token_provider=lambda: "tok" ) _assert_credential_chain_off(client) @pytest.fixture def profile_config_dir(tmp_path: pathlib.Path) -> pathlib.Path: """A fully-resolvable Anthropic profile (config + oauth credentials + base_url) that the base client's credential chain would adopt if it ever ran.""" (tmp_path / "configs").mkdir() (tmp_path / "credentials").mkdir() (tmp_path / "configs" / "default.json").write_text( json.dumps( { "version": "1.0", "organization_id": "org_profile_should_not_leak", "base_url": "https://profile-gateway.example.com", "authentication": {"type": "user_oauth"}, } ) ) (tmp_path / "credentials" / "default.json").write_text( json.dumps( { "version": "1.0", "type": "oauth_token", "access_token": "profile-oauth-token-should-not-leak", "refresh_token": "profile-refresh-token", "expires_at": 4102444800000, } ) ) return tmp_path @pytest.mark.respx() def test_resolvable_profile_cannot_leak( respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch, profile_config_dir: pathlib.Path ) -> None: # An ANTHROPIC_PROFILE / ANTHROPIC_CONFIG_DIR pointing at a real on-disk profile # must not be able to attach its base_url or oauth token to this client's requests. monkeypatch.setenv("ANTHROPIC_CONFIG_DIR", str(profile_config_dir)) monkeypatch.setenv("ANTHROPIC_PROFILE", "default") respx_mock.post(re.compile(r"https://claude\.googleapis\.com/.*")).mock( return_value=httpx.Response(200, json={"foo": "bar"}) ) client = AnthropicGoogleCloud( project="my-project", location="us-central1", workspace_id="wrkspc_x", token_provider=lambda: "gcp-token" ) _assert_credential_chain_off(client) client.messages.create(max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5") calls = cast("list[Any]", respx_mock.calls) req = calls[0].request assert req.url.host == "claude.googleapis.com" assert "profile-gateway.example.com" not in str(req.url) assert req.headers.get_list("authorization") == ["Bearer gcp-token"] assert "profile-oauth-token-should-not-leak" not in repr(req.headers) @pytest.mark.respx() async def test_resolvable_profile_cannot_leak_async( respx_mock: MockRouter, monkeypatch: pytest.MonkeyPatch, profile_config_dir: pathlib.Path ) -> None: monkeypatch.setenv("ANTHROPIC_CONFIG_DIR", str(profile_config_dir)) monkeypatch.setenv("ANTHROPIC_PROFILE", "default") respx_mock.post(re.compile(r"https://example\.test/.*")).mock(return_value=httpx.Response(200, json={"foo": "bar"})) client = AsyncAnthropicGoogleCloud( base_url="https://example.test/", workspace_id="wrkspc_x", token_provider=lambda: "gcp-token" ) _assert_credential_chain_off(client) await client.messages.create(max_tokens=16, messages=[{"role": "user", "content": "hi"}], model="claude-haiku-4-5") calls = cast("list[Any]", respx_mock.calls) req = calls[0].request assert req.headers.get_list("authorization") == ["Bearer gcp-token"] assert "profile-oauth-token-should-not-leak" not in repr(req.headers) # --------------------------------------------------------------------------- # Async init parity # --------------------------------------------------------------------------- class TestAsyncAnthropicGoogleCloud: async def test_workspace_required(self) -> None: with pytest.raises(ValueError, match="workspace ID"): AsyncAnthropicGoogleCloud(base_url="https://example.test/", token_provider=lambda: "tok") async def test_api_key_and_auth_token_rejected(self) -> None: with pytest.raises(TypeError): AsyncAnthropicGoogleCloud(base_url="https://example.test/", workspace_id="wrkspc_x", api_key="sk-ant-x") # type: ignore[call-arg] with pytest.raises(TypeError): AsyncAnthropicGoogleCloud(base_url="https://example.test/", workspace_id="wrkspc_x", auth_token="tok") # type: ignore[call-arg] async def test_skip_auth_mutually_exclusive_with_credentials(self) -> None: with pytest.raises(ValueError, match="mutually exclusive"): AsyncAnthropicGoogleCloud(base_url="https://example.test/", skip_auth=True, token_provider=lambda: "tok") async def test_completions_is_none(self) -> None: client = AsyncAnthropicGoogleCloud( base_url="https://example.test/", workspace_id="wrkspc_x", token_provider=lambda: "tok" ) assert client.completions is None async def test_base_url_derived(self) -> None: client = AsyncAnthropicGoogleCloud( project="my-project", location="us-central1", workspace_id="wrkspc_x", token_provider=lambda: "tok" ) assert str(client.base_url) == DERIVED_BASE_URL async def test_location_defaults_to_global(self) -> None: client = AsyncAnthropicGoogleCloud(project="my-project", workspace_id="wrkspc_x", token_provider=lambda: "tok") assert str(client.base_url) == GLOBAL_DERIVED_BASE_URL async def test_missing_project_defers_without_construct_error(self) -> None: AsyncAnthropicGoogleCloud(location="us-central1", workspace_id="wrkspc_x") async def test_skip_auth_deriving_base_url_requires_project(self) -> None: with pytest.raises(ValueError, match="project"): AsyncAnthropicGoogleCloud(location="us-central1", workspace_id="wrkspc_x", skip_auth=True) async def test_skip_auth_deriving_base_url_requires_workspace(self) -> None: with pytest.raises(ValueError, match=r"(?s)workspace_id.*ANTHROPIC_GOOGLE_CLOUD_WORKSPACE_ID"): AsyncAnthropicGoogleCloud(project="my-project", skip_auth=True) async def test_skip_auth_derives_url_with_workspace(self) -> None: client = AsyncAnthropicGoogleCloud(project="my-project", workspace_id="wrkspc_x", skip_auth=True) assert str(client.base_url) == GLOBAL_DERIVED_BASE_URL anthropic-sdk-python-0.120.2/tests/lib/test_google_cloud_live.py000066400000000000000000000104311523216435200247450ustar00rootroot00000000000000"""Live integration tests against the real gateway. Gated on ``ANTHROPIC_LIVE=1``; the regular suite never hits the network. Expects ``ANTHROPIC_GOOGLE_CLOUD_{PROJECT,WORKSPACE_ID}`` (or ``..._BASE_URL``) and Application Default Credentials in the environment. """ from __future__ import annotations import os from typing import Iterator, AsyncIterator import httpx import pytest from anthropic import APIStatusError from anthropic.lib.google_cloud import AnthropicGoogleCloud, AsyncAnthropicGoogleCloud pytestmark = [ pytest.mark.skipif(os.environ.get("ANTHROPIC_LIVE") != "1", reason="Set ANTHROPIC_LIVE=1 to run live tests"), # google-auth emits a Python-EOL FutureWarning on import; the repo runs with # filterwarnings=error, so silence it for this live-test module. pytest.mark.filterwarnings("ignore::FutureWarning"), pytest.mark.filterwarnings("ignore::DeprecationWarning"), ] MODEL = "claude-haiku-4-5" LOCATION = os.environ.get("ANTHROPIC_GOOGLE_CLOUD_LOCATION", "us-central1") @pytest.fixture def sync_client() -> Iterator[AnthropicGoogleCloud]: client = AnthropicGoogleCloud( location=LOCATION, max_retries=1, http_client=httpx.Client(timeout=60.0), ) try: yield client finally: client.close() @pytest.fixture async def async_client() -> AsyncIterator[AsyncAnthropicGoogleCloud]: client = AsyncAnthropicGoogleCloud( location=LOCATION, max_retries=1, http_client=httpx.AsyncClient(timeout=60.0), ) try: yield client finally: await client.close() class TestSyncLive: def test_non_streaming(self, sync_client: AnthropicGoogleCloud) -> None: message = sync_client.messages.create( model=MODEL, max_tokens=32, messages=[{"role": "user", "content": "Say hello in one word."}], ) assert message.role == "assistant" assert message.content[0].type == "text" assert message.usage.output_tokens > 0 def test_streaming(self, sync_client: AnthropicGoogleCloud) -> None: events: list[str] = [] with sync_client.messages.stream( model=MODEL, max_tokens=32, messages=[{"role": "user", "content": "Say hello in one word."}], ) as stream: for event in stream: events.append(event.type) final = stream.get_final_message() assert events[0] == "message_start" assert "content_block_delta" in events assert events[-1] == "message_stop" assert final.role == "assistant" assert final.content[0].type == "text" def test_bad_model_surfaces_typed_error(self, sync_client: AnthropicGoogleCloud) -> None: with pytest.raises(APIStatusError): sync_client.messages.create( model="not-a-real-model", max_tokens=16, messages=[{"role": "user", "content": "hi"}], ) class TestAsyncLive: async def test_non_streaming(self, async_client: AsyncAnthropicGoogleCloud) -> None: message = await async_client.messages.create( model=MODEL, max_tokens=32, messages=[{"role": "user", "content": "Say hello in one word."}], ) assert message.role == "assistant" assert message.content[0].type == "text" async def test_streaming(self, async_client: AsyncAnthropicGoogleCloud) -> None: events: list[str] = [] async with async_client.messages.stream( model=MODEL, max_tokens=32, messages=[{"role": "user", "content": "Say hello in one word."}], ) as stream: async for event in stream: events.append(event.type) final = await stream.get_final_message() assert events[0] == "message_start" assert "content_block_delta" in events assert events[-1] == "message_stop" assert final.content[0].type == "text" async def test_bad_model_surfaces_typed_error(self, async_client: AsyncAnthropicGoogleCloud) -> None: with pytest.raises(APIStatusError): await async_client.messages.create( model="not-a-real-model", max_tokens=16, messages=[{"role": "user", "content": "hi"}], ) anthropic-sdk-python-0.120.2/tests/lib/test_refusal_fallback.py000066400000000000000000001001661523216435200245510ustar00rootroot00000000000000from __future__ import annotations import os import json import logging import threading from typing import Any, List, Protocol, cast import httpx import pytest from respx import MockRouter from anthropic import ( Omit, Anthropic, AnthropicError, AsyncAnthropic, BetaFallbackState, BetaRefusalFallbackMiddleware, omit, ) from anthropic.types.beta import BetaMessage, BetaFallbackParam, BetaOutputConfigParam from anthropic.lib.middleware._fallbacks import _fallback_state from anthropic.types.anthropic_beta_param import AnthropicBetaParam base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") api_key = "my-anthropic-api-key" LOGGER_NAME = "anthropic.lib.middleware" def make_sync_client(**kwargs: Any) -> Anthropic: return Anthropic(base_url=base_url, api_key=api_key, _strict_response_validation=True, max_retries=0, **kwargs) def make_async_client(**kwargs: Any) -> AsyncAnthropic: return AsyncAnthropic(base_url=base_url, api_key=api_key, _strict_response_validation=True, max_retries=0, **kwargs) def message(model: str, **overrides: Any) -> httpx.Response: return httpx.Response( 200, json={ "id": "msg_1", "type": "message", "role": "assistant", "model": model, "content": [], "stop_reason": "end_turn", "stop_sequence": None, "usage": {"input_tokens": 1, "output_tokens": 1}, **overrides, }, ) def refusal(model: str, fallback_credit_token: str | None = None) -> httpx.Response: return message( model, stop_reason="refusal", stop_details={ "type": "refusal", "category": None, "explanation": None, "fallback_credit_token": fallback_credit_token, }, ) class MockRequestCall(Protocol): request: httpx.Request def create_message( client: Anthropic, *, betas: List[AnthropicBetaParam] | Omit = omit, fallbacks: List[BetaFallbackParam] | Omit = omit, output_config: BetaOutputConfigParam | Omit = omit, ) -> BetaMessage: return client.beta.messages.create( model="primary-model", max_tokens=1024, messages=[{"role": "user", "content": "hi"}], betas=betas, fallbacks=fallbacks, output_config=output_config, ) async def create_message_async(client: AsyncAnthropic) -> BetaMessage: return await client.beta.messages.create( model="primary-model", max_tokens=1024, messages=[{"role": "user", "content": "hi"}], ) def request_bodies(respx_mock: MockRouter) -> list[dict[str, Any]]: calls = cast("list[MockRequestCall]", respx_mock.calls) return [cast("dict[str, Any]", json.loads(call.request.content)) for call in calls] def beta_headers(respx_mock: MockRouter) -> list[str | None]: calls = cast("list[MockRequestCall]", respx_mock.calls) return [call.request.headers.get("anthropic-beta") for call in calls] class TestRefusalFallback: @pytest.mark.respx(base_url=base_url) def test_retries_a_refusal_with_the_fallback_params_and_credit_token(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( side_effect=[refusal("primary-model", "credit-token"), message("fallback-model")] ) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware([{"model": "fallback-model"}])]) result = create_message(client) assert result.model == "fallback-model" assert result.stop_reason == "end_turn" # a `fallback` seam block is prepended at the model boundary — the same # block shape the streaming splice emits assert [block.to_dict() for block in result.content] == [ {"type": "fallback", "from": {"model": "primary-model"}, "to": {"model": "fallback-model"}, "trigger": {"type": "refusal", "category": None}} ] bodies = request_bodies(respx_mock) assert [body["model"] for body in bodies] == ["primary-model", "fallback-model"] assert bodies[1]["fallback_credit_token"] == {"token": "credit-token", "mode": "best_effort"} @pytest.mark.respx(base_url=base_url) def test_pins_the_conversation_to_the_accepted_fallback_via_state( self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture ) -> None: respx_mock.post("/v1/messages").mock( side_effect=[refusal("primary-model"), message("fallback-model"), message("fallback-model")] ) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware([{"model": "fallback-model"}])]) state = BetaFallbackState() with caplog.at_level(logging.WARNING, logger=LOGGER_NAME): with state: create_message(client) assert state.index == 0 # the follow-up goes straight to the pinned fallback in a single request with state: create_message(client) bodies = request_bodies(respx_mock) assert [body["model"] for body in bodies] == ["primary-model", "fallback-model", "fallback-model"] assert not [record for record in caplog.records if record.name == LOGGER_NAME] @pytest.mark.respx(base_url=base_url, assert_all_called=False) def test_raises_if_state_index_is_out_of_bounds_for_the_chain(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[message("fallback-model")]) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware([{"model": "fallback-model"}])]) state = BetaFallbackState() state.index = 1 with state: with pytest.raises( AnthropicError, match=r"BetaFallbackState\.index 1 is out of bounds for a chain of 1 fallback\(s\)" ): create_message(client) assert len(respx_mock.calls) == 0 @pytest.mark.respx(base_url=base_url) def test_warns_once_when_falling_back_without_a_state( self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture ) -> None: respx_mock.post("/v1/messages").mock( side_effect=[ refusal("primary-model"), message("fallback-model"), refusal("primary-model"), message("fallback-model"), ] ) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware([{"model": "fallback-model"}])]) with caplog.at_level(logging.WARNING, logger=LOGGER_NAME): create_message(client) create_message(client) warnings = [record for record in caplog.records if record.name == LOGGER_NAME] assert len(warnings) == 1 assert "BetaFallbackState" in warnings[0].getMessage() @pytest.mark.respx(base_url=base_url) def test_a_separate_conversation_is_unaffected_by_another_state(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( side_effect=[refusal("primary-model"), message("fallback-model"), message("primary-model")] ) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware([{"model": "fallback-model"}])]) with BetaFallbackState(): create_message(client) with BetaFallbackState(): create_message(client) bodies = request_bodies(respx_mock) assert [body["model"] for body in bodies] == ["primary-model", "fallback-model", "primary-model"] @pytest.mark.respx(base_url=base_url) def test_leaves_accepted_requests_and_the_response_untouched(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[message("primary-model")]) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware([{"model": "fallback-model"}])]) state = BetaFallbackState() with state: result = create_message(client) assert result.model == "primary-model" bodies = request_bodies(respx_mock) assert len(bodies) == 1 assert "fallback_credit_token" not in bodies[0] assert state.index is None @pytest.mark.respx(base_url=base_url) def test_walks_each_hop_through_the_chain_until_a_model_accepts(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( side_effect=[ refusal("primary-model"), refusal("mid-model"), message("last-model", content=[{"type": "text", "text": "ok"}]), ] ) client = make_sync_client( middleware=[BetaRefusalFallbackMiddleware([{"model": "mid-model"}, {"model": "last-model"}])] ) state = BetaFallbackState() with state: result = create_message(client) assert result.model == "last-model" assert state.index == 1 # one seam per model boundary, in hop order, ahead of the served content assert [block.to_dict() for block in result.content] == [ {"type": "fallback", "from": {"model": "primary-model"}, "to": {"model": "mid-model"}, "trigger": {"type": "refusal", "category": None}}, {"type": "fallback", "from": {"model": "mid-model"}, "to": {"model": "last-model"}, "trigger": {"type": "refusal", "category": None}}, {"type": "text", "text": "ok"}, ] bodies = request_bodies(respx_mock) assert [body["model"] for body in bodies] == ["primary-model", "mid-model", "last-model"] @pytest.mark.respx(base_url=base_url) def test_a_pinned_continuation_seams_from_the_pinned_model(self, respx_mock: MockRouter) -> None: # the pinned entry refuses and the chain advances — the first seam's # `from.model` must be the pinned entry (the model actually queried), # not the caller's original body model respx_mock.post("/v1/messages").mock( side_effect=[refusal("mid-model"), message("last-model", content=[{"type": "text", "text": "ok"}])] ) client = make_sync_client( middleware=[BetaRefusalFallbackMiddleware([{"model": "mid-model"}, {"model": "last-model"}])] ) state = BetaFallbackState() state.index = 0 with state: result = create_message(client) assert result.model == "last-model" assert [block.to_dict() for block in result.content] == [ {"type": "fallback", "from": {"model": "mid-model"}, "to": {"model": "last-model"}, "trigger": {"type": "refusal", "category": None}}, {"type": "text", "text": "ok"}, ] bodies = request_bodies(respx_mock) assert [body["model"] for body in bodies] == ["mid-model", "last-model"] @pytest.mark.respx(base_url=base_url) def test_returns_the_final_refusal_once_the_chain_is_exhausted(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[refusal("primary-model"), refusal("fallback-model")]) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware([{"model": "fallback-model"}])]) result = create_message(client) assert result.model == "fallback-model" assert result.stop_reason == "refusal" # terminal refusal is surfaced verbatim — no seam blocks prepended assert result.content == [] assert len(respx_mock.calls) == 2 @pytest.mark.respx(base_url=base_url) def test_entry_overrides_are_merged_whole_over_the_body(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[refusal("primary-model"), message("fallback-model")]) client = make_sync_client( middleware=[BetaRefusalFallbackMiddleware([{"model": "fallback-model", "max_tokens": 32}])] ) create_message(client) bodies = request_bodies(respx_mock) assert bodies[0]["max_tokens"] == 1024 assert bodies[1]["max_tokens"] == 32 @pytest.mark.respx(base_url=base_url) def test_an_explicit_none_entry_field_unsets_it_on_the_retry(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[refusal("primary-model"), message("fallback-model")]) client = make_sync_client( middleware=[BetaRefusalFallbackMiddleware([{"model": "fallback-model", "max_tokens": None}])] ) create_message(client) bodies = request_bodies(respx_mock) assert bodies[0]["max_tokens"] == 1024 # explicit None unsets: absent from the retried request, not sent as null assert "max_tokens" not in bodies[1] @pytest.mark.respx(base_url=base_url) def test_each_hop_patches_the_original_params_not_the_previous_hop(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( side_effect=[refusal("primary-model"), refusal("mid-model"), message("last-model")] ) client = make_sync_client( middleware=[ BetaRefusalFallbackMiddleware([{"model": "mid-model", "max_tokens": 32}, {"model": "last-model"}]) ] ) create_message(client) bodies = request_bodies(respx_mock) assert bodies[0]["max_tokens"] == 1024 assert bodies[1]["max_tokens"] == 32 # hop 2 patches the ORIGINAL request — hop 1's override does not leak, # and its absent field keeps the original value assert bodies[2]["max_tokens"] == 1024 assert bodies[2]["model"] == "last-model" @pytest.mark.respx(base_url=base_url) def test_output_config_subfields_patch_one_level_deep(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[refusal("primary-model"), message("fallback-model")]) client = make_sync_client( middleware=[ BetaRefusalFallbackMiddleware( [{"model": "fallback-model", "output_config": {"effort": "high", "format": None}}] ) ] ) create_message( client, output_config={ "effort": "low", "format": {"type": "json_schema", "schema": {"type": "object"}}, "task_budget": {"type": "tokens", "total": 500}, }, ) bodies = request_bodies(respx_mock) # `effort` set overrides, `format: None` unsets only `format`, the # absent `task_budget` keeps its original value assert bodies[1]["output_config"] == {"effort": "high", "task_budget": {"type": "tokens", "total": 500}} @pytest.mark.respx(base_url=base_url) def test_an_explicit_none_output_config_unsets_it_whole(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[refusal("primary-model"), message("fallback-model")]) client = make_sync_client( middleware=[BetaRefusalFallbackMiddleware([{"model": "fallback-model", "output_config": None}])] ) create_message(client, output_config={"effort": "low"}) bodies = request_bodies(respx_mock) assert bodies[0]["output_config"] == {"effort": "low"} assert "output_config" not in bodies[1] @pytest.mark.respx(base_url=base_url) def test_an_absent_output_config_keeps_the_original(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[refusal("primary-model"), message("fallback-model")]) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware([{"model": "fallback-model"}])]) create_message(client, output_config={"effort": "low"}) assert request_bodies(respx_mock)[1]["output_config"] == {"effort": "low"} @pytest.mark.respx(base_url=base_url) def test_output_config_subfields_seed_a_missing_original(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[refusal("primary-model"), message("fallback-model")]) client = make_sync_client( middleware=[ BetaRefusalFallbackMiddleware( [{"model": "fallback-model", "output_config": {"effort": "high", "format": None}}] ) ] ) create_message(client) bodies = request_bodies(respx_mock) assert "output_config" not in bodies[0] # the subfields the hop sets seed a new object; None entries are dropped assert bodies[1]["output_config"] == {"effort": "high"} @pytest.mark.respx(base_url=base_url) def test_unsetting_every_output_config_subfield_drops_the_key(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[refusal("primary-model"), message("fallback-model")]) client = make_sync_client( middleware=[ BetaRefusalFallbackMiddleware( [{"model": "fallback-model", "output_config": {"effort": None, "format": None}}] ) ] ) create_message( client, output_config={"effort": "low", "format": {"type": "json_schema", "schema": {"type": "object"}}}, ) bodies = request_bodies(respx_mock) assert bodies[0]["output_config"] # nothing left after the unsets — the key is dropped, never sent as `{}` assert "output_config" not in bodies[1] @pytest.mark.respx(base_url=base_url) def test_all_none_output_config_subfields_on_a_missing_original_add_nothing(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[refusal("primary-model"), message("fallback-model")]) client = make_sync_client( middleware=[BetaRefusalFallbackMiddleware([{"model": "fallback-model", "output_config": {"effort": None}}])] ) create_message(client) bodies = request_bodies(respx_mock) assert "output_config" not in bodies[0] # an empty result is never added as `{}` assert "output_config" not in bodies[1] @pytest.mark.respx(base_url=base_url) def test_output_config_subfields_do_not_leak_into_the_next_hop(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( side_effect=[refusal("primary-model"), refusal("mid-model"), message("last-model")] ) client = make_sync_client( middleware=[ BetaRefusalFallbackMiddleware( [{"model": "mid-model", "output_config": {"effort": "high"}}, {"model": "last-model"}] ) ] ) create_message(client, output_config={"effort": "low"}) bodies = request_bodies(respx_mock) assert bodies[1]["output_config"] == {"effort": "high"} # hop 2 patches the ORIGINAL output_config — hop 1's subfield does not leak assert bodies[2]["output_config"] == {"effort": "low"} @pytest.mark.respx(base_url=base_url) def test_a_hop_http_error_surfaces_to_the_app(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( side_effect=[ refusal("primary-model", "credit-token"), httpx.Response( 400, json={"type": "error", "error": {"type": "invalid_request_error", "message": "nope"}} ), ] ) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware([{"model": "fallback-model"}])]) from anthropic import BadRequestError with pytest.raises(BadRequestError): create_message(client) assert len(respx_mock.calls) == 2 @pytest.mark.respx(base_url=base_url, assert_all_called=False) def test_server_side_fallbacks_raise_an_error(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[refusal("primary-model", "credit-token")]) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware([{"model": "fallback-model"}])]) with pytest.raises( AnthropicError, match=r"Sending the `fallbacks:` request param is not supported when using the `BetaRefusalFallbackMiddleware`\. You should either remove the middleware and send `fallbacks:` with the `server-side-fallback-2026-07-01` beta header to let the API handle refusal fallbacks, or omit the `fallbacks:` param if you'd like `BetaRefusalFallbackMiddleware` to handle fallbacks on the client side\.", ): create_message(client, fallbacks=[{"model": "server-fallback"}]) # the error is raised before any request is sent assert len(respx_mock.calls) == 0 @pytest.mark.respx(base_url=base_url) def test_an_empty_chain_disables_the_middleware(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[refusal("primary-model", "credit-token")]) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware([])]) result = create_message(client) assert result.stop_reason == "refusal" assert len(respx_mock.calls) == 1 assert beta_headers(respx_mock) == [None] @pytest.mark.respx(base_url=base_url) def test_the_non_beta_messages_surface_passes_through(self, respx_mock: MockRouter) -> None: # only `client.beta.messages` requests are handled; the first-party # surface mints no credit tokens, so its refusals surface as-is respx_mock.post("/v1/messages").mock( side_effect=[ message( "primary-model", stop_reason="refusal", stop_details={"type": "refusal", "category": None, "explanation": None}, ) ] ) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware([{"model": "fallback-model"}])]) result = client.messages.create( model="primary-model", max_tokens=1024, messages=[{"role": "user", "content": "hi"}], ) assert result.stop_reason == "refusal" assert len(respx_mock.calls) == 1 assert beta_headers(respx_mock) == [None] class TestBetaHeader: @pytest.mark.respx(base_url=base_url) def test_sends_the_fallback_credit_beta_on_the_original_and_fallback_requests(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( side_effect=[refusal("primary-model", "credit-token"), message("fallback-model")] ) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware([{"model": "fallback-model"}])]) create_message(client) assert beta_headers(respx_mock) == ["fallback-credit-2026-07-01", "fallback-credit-2026-07-01"] @pytest.mark.respx(base_url=base_url) def test_the_betas_option_replaces_the_default(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[message("primary-model")]) client = make_sync_client( middleware=[ BetaRefusalFallbackMiddleware( [{"model": "fallback-model"}], betas=["fallback-credit-2027-01-01", "interleaved-thinking-2025-05-14"], ) ] ) create_message(client) assert beta_headers(respx_mock) == ["fallback-credit-2027-01-01, interleaved-thinking-2025-05-14"] @pytest.mark.respx(base_url=base_url) def test_empty_betas_sends_no_beta_header(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[message("primary-model")]) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware([{"model": "fallback-model"}], betas=[])]) create_message(client) assert beta_headers(respx_mock) == [None] @pytest.mark.respx(base_url=base_url) def test_does_not_duplicate_a_beta_already_on_the_request(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[message("primary-model")]) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware([{"model": "fallback-model"}])]) create_message(client, betas=["fallback-credit-2026-07-01"]) assert beta_headers(respx_mock) == ["fallback-credit-2026-07-01"] @pytest.mark.respx(base_url=base_url) def test_appends_to_betas_already_on_the_request(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[message("primary-model")]) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware([{"model": "fallback-model"}])]) create_message(client, betas=["interleaved-thinking-2025-05-14"]) assert beta_headers(respx_mock) == ["interleaved-thinking-2025-05-14, fallback-credit-2026-07-01"] def helper_headers(respx_mock: MockRouter) -> list[list[str]]: calls = cast("list[MockRequestCall]", respx_mock.calls) return [call.request.headers.get_list("x-stainless-helper") for call in calls] class TestHelperTelemetry: @pytest.mark.respx(base_url=base_url) def test_tags_the_original_and_fallback_requests(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( side_effect=[refusal("primary-model", "credit-token"), message("fallback-model")] ) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware([{"model": "fallback-model"}])]) create_message(client) assert helper_headers(respx_mock) == [ ["fallback-refusal-middleware"], ["fallback-refusal-middleware"], ] @pytest.mark.respx(base_url=base_url) def test_appends_to_a_helper_tag_already_on_the_request(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[message("primary-model")]) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware([{"model": "fallback-model"}])]) client.beta.messages.create( model="primary-model", max_tokens=1024, messages=[{"role": "user", "content": "hi"}], extra_headers={"X-Stainless-Helper": "BetaToolRunner"}, ) assert helper_headers(respx_mock) == [["BetaToolRunner, fallback-refusal-middleware"]] @pytest.mark.respx(base_url=base_url) def test_does_not_tag_requests_it_passes_through(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[message("primary-model")]) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware([{"model": "fallback-model"}])]) # the GA surface is not applicable to this middleware client.messages.create( model="primary-model", max_tokens=1024, messages=[{"role": "user", "content": "hi"}], ) assert helper_headers(respx_mock) == [[]] @pytest.mark.respx(base_url=base_url) async def test_async_tags_the_original_and_fallback_requests(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( side_effect=[refusal("primary-model", "credit-token"), message("fallback-model")] ) client = make_async_client(middleware=[BetaRefusalFallbackMiddleware([{"model": "fallback-model"}])]) await create_message_async(client) assert helper_headers(respx_mock) == [ ["fallback-refusal-middleware"], ["fallback-refusal-middleware"], ] class TestAsyncRefusalFallback: @pytest.mark.respx(base_url=base_url) async def test_retries_a_refusal_with_the_fallback_params_and_credit_token(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( side_effect=[refusal("primary-model", "credit-token"), message("fallback-model")] ) client = make_async_client(middleware=[BetaRefusalFallbackMiddleware([{"model": "fallback-model"}])]) result = await create_message_async(client) assert result.model == "fallback-model" assert [block.to_dict() for block in result.content] == [ {"type": "fallback", "from": {"model": "primary-model"}, "to": {"model": "fallback-model"}, "trigger": {"type": "refusal", "category": None}} ] bodies = request_bodies(respx_mock) assert [body["model"] for body in bodies] == ["primary-model", "fallback-model"] assert bodies[1]["fallback_credit_token"] == {"token": "credit-token", "mode": "best_effort"} @pytest.mark.respx(base_url=base_url) async def test_pins_the_conversation_to_the_accepted_fallback_via_state(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( side_effect=[refusal("primary-model"), message("fallback-model"), message("fallback-model")] ) client = make_async_client(middleware=[BetaRefusalFallbackMiddleware([{"model": "fallback-model"}])]) state = BetaFallbackState() with state: await create_message_async(client) assert state.index == 0 with state: await create_message_async(client) bodies = request_bodies(respx_mock) assert [body["model"] for body in bodies] == ["primary-model", "fallback-model", "fallback-model"] @pytest.mark.respx(base_url=base_url) async def test_walks_each_hop_through_the_chain_until_a_model_accepts(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( side_effect=[refusal("primary-model"), refusal("mid-model"), message("last-model")] ) client = make_async_client( middleware=[BetaRefusalFallbackMiddleware([{"model": "mid-model"}, {"model": "last-model"}])] ) state = BetaFallbackState() with state: result = await create_message_async(client) assert result.model == "last-model" assert state.index == 1 class TestBetaFallbackState: def test_reentering_the_same_state_nests(self) -> None: state = BetaFallbackState() with state: with state: assert _fallback_state.get() is state assert _fallback_state.get() is state assert _fallback_state.get() is None def test_nesting_different_states_restores_the_outer_pin(self) -> None: outer, inner = BetaFallbackState(), BetaFallbackState() with outer: with inner: assert _fallback_state.get() is inner assert _fallback_state.get() is outer assert _fallback_state.get() is None def test_one_state_shared_across_threads(self) -> None: # Each thread runs in its own context, so a state shared between them — # the documented usage — must enter and exit with that context's own # tokens; an instance-level token stack would interleave them and # `ContextVar.reset` raises on a foreign context's token. state = BetaFallbackState() a_entered, b_entered, a_exited = threading.Event(), threading.Event(), threading.Event() errors: list[Exception] = [] def thread_a() -> None: try: with state: a_entered.set() assert b_entered.wait(timeout=5) except Exception as err: errors.append(err) finally: a_entered.set() a_exited.set() def thread_b() -> None: try: assert a_entered.wait(timeout=5) with state: b_entered.set() assert a_exited.wait(timeout=5) assert _fallback_state.get() is None except Exception as err: errors.append(err) finally: b_entered.set() threads = [threading.Thread(target=thread_a), threading.Thread(target=thread_b)] for thread in threads: thread.start() for thread in threads: thread.join(timeout=10) assert errors == [] class TestErrorLogging: @pytest.mark.respx(base_url=base_url) def test_nothing_is_logged_for_non_streaming_retries( self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture ) -> None: respx_mock.post("/v1/messages").mock( side_effect=[refusal("primary-model", "credit-token"), message("fallback-model")] ) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware([{"model": "fallback-model"}])]) create_message(client) assert not [ record for record in caplog.records if record.name == LOGGER_NAME and record.levelno >= logging.ERROR ] anthropic-sdk-python-0.120.2/tests/lib/test_refusal_fallback_streaming.py000066400000000000000000001771751523216435200266400ustar00rootroot00000000000000from __future__ import annotations import os import re import json import logging from typing import Any, List, Protocol, cast from pathlib import Path import httpx import pytest from respx import MockRouter from anthropic import ( Omit, Stream, Anthropic, APIRequest, APIResponse, AsyncStream, AnthropicError, AsyncAnthropic, BetaFallbackState, BetaRefusalFallbackMiddleware, omit, ) from anthropic._models import FinalRequestOptions from anthropic.types.beta import BetaMessage, BetaFallbackParam, BetaRawMessageStreamEvent from anthropic.types.anthropic_beta_param import AnthropicBetaParam base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") api_key = "my-anthropic-api-key" LOGGER_NAME = "anthropic.lib.middleware" def error_logs(caplog: pytest.LogCaptureFixture) -> list[str]: """Messages the middleware logged at ERROR — refusals surfaced rather than retried.""" return [ record.getMessage() for record in caplog.records if record.name == LOGGER_NAME and record.levelno >= logging.ERROR ] FIXTURES = Path(__file__).parent.parent / "fixtures" / "fable-fallback" FALLBACK_MODEL = "claude-opus-4-8" SECOND_MODEL = "claude-sonnet-4-6" FALLBACKS: List[BetaFallbackParam] = [{"model": FALLBACK_MODEL}] TWO_FALLBACKS: List[BetaFallbackParam] = [{"model": FALLBACK_MODEL}, {"model": SECOND_MODEL}] # Wire-shaped synthetic capture — the primary refuses after a thinking + # partial-text block and mints a credit token; the fallback then completes # the message. STREAM_A = (FIXTURES / "stream-a-refusal.sse").read_text() STREAM_B = (FIXTURES / "stream-b-fallback.sse").read_text() # Server-tool wire (synthetic, wire-shaped): server_tool_use streams its input # via input_json_delta after an empty `input:{}`, the web_search_tool_result # arrives as a single content_block_start carrying full content, and the # refusal terminal (message_delta + token) lands mid-tool-loop, after a # partial text block. The token is never redeemed (the mock serves the next # leg). STREAM_A_TOOL = (FIXTURES / "stream-a-toolrefusal.sse").read_text() PARAMS: dict[str, Any] = { "model": "claude-fable-5", "max_tokens": 1024, "messages": [{"role": "user", "content": "Hey claudius! Can you tell me what a solar eclipse is?"}], } def make_sync_client(**kwargs: Any) -> Anthropic: return Anthropic(base_url=base_url, api_key=api_key, _strict_response_validation=True, max_retries=0, **kwargs) def make_async_client(**kwargs: Any) -> AsyncAnthropic: return AsyncAnthropic(base_url=base_url, api_key=api_key, _strict_response_validation=True, max_retries=0, **kwargs) def make_lenient_client(**kwargs: Any) -> Anthropic: """A client with the default (non-strict) response validation. The fixture wire's terminal refusal delta self-reports a `{type: "message"}` iteration without a `model` key, which the generated `BetaMessageIterationUsage` type marks required — strict validation rejects it. Tests where that wire reaches the parser verbatim (pass-through refusals, synthetic closes reusing the refusal's usage) use this client. """ return Anthropic(base_url=base_url, api_key=api_key, max_retries=0, **kwargs) def sse_response(body: str) -> httpx.Response: return httpx.Response(200, headers={"content-type": "text/event-stream"}, content=body.encode("utf-8")) def json_response(body: Any, status: int) -> httpx.Response: return httpx.Response(status, json=body) def error_response(message: str, status: int) -> httpx.Response: return json_response({"type": "error", "error": {"type": "invalid_request_error", "message": message}}, status) def ev(data: dict[str, Any]) -> str: """Serialize one event payload as an SSE frame (its `type` is the event name).""" return f"event: {data['type']}\ndata: {json.dumps(data)}\n\n" def message_start() -> str: return ev( { "type": "message_start", "message": { "id": "msg_a", "type": "message", "role": "assistant", "model": "claude-fable-5", "content": [], "stop_reason": None, "stop_sequence": None, "usage": {"input_tokens": 12, "output_tokens": 1}, }, } ) def refusal_delta(token: str | None = "tok_abc", has_prefill_claim: bool = True) -> str: return ev( { "type": "message_delta", "delta": { "stop_reason": "refusal", "stop_sequence": None, "stop_details": { "type": "refusal", "category": None, "explanation": None, "fallback_credit_token": token, "fallback_has_prefill_claim": has_prefill_claim if token is not None else None, }, }, "usage": {"output_tokens": 20}, } ) def redeemed_token(token: str) -> dict[str, str]: """The request-body form of a redeemed credit token — the object shape, best-effort mode.""" return {"token": token, "mode": "best_effort"} def collect(stream: Stream[BetaRawMessageStreamEvent]) -> list[BetaRawMessageStreamEvent]: return list(stream) class MockRequestCall(Protocol): request: httpx.Request def request_bodies(respx_mock: MockRouter) -> list[dict[str, Any]]: calls = cast("list[MockRequestCall]", respx_mock.calls) return [cast("dict[str, Any]", json.loads(call.request.content)) for call in calls] def beta_headers(respx_mock: MockRouter) -> list[str | None]: calls = cast("list[MockRequestCall]", respx_mock.calls) return [call.request.headers.get("anthropic-beta") for call in calls] def skeleton(events: list[BetaRawMessageStreamEvent]) -> list[str]: """Compact structural skeleton of a spliced stream — no text content.""" out: list[str] = [] for event in events: if event.type == "content_block_start": block = event.content_block if block.type == "fallback": label = f"fallback{{{block.from_.model}->{block.to.model}}}" else: label = block.type out.append(f"start[{event.index}] {label}") elif event.type == "content_block_delta": out.append(f"delta[{event.index}] {event.delta.type}") elif event.type == "content_block_stop": out.append(f"stop[{event.index}]") elif event.type == "message_delta": iterations = ",".join(f"{i.type}:{i.model}" for i in (event.usage.iterations or [])) # type: ignore[union-attr] out.append(f"message_delta {event.delta.stop_reason} iter=[{iterations}]") else: out.append(event.type) return out def block_starts(events: list[BetaRawMessageStreamEvent]) -> list[tuple[int, str]]: return [(e.index, e.content_block.type) for e in events if e.type == "content_block_start"] def create_stream( client: Anthropic, *, betas: List[AnthropicBetaParam] | Omit = omit ) -> Stream[BetaRawMessageStreamEvent]: return client.beta.messages.create( model="claude-fable-5", max_tokens=1024, messages=[{"role": "user", "content": "Hey claudius! Can you tell me what a solar eclipse is?"}], betas=betas, stream=True, ) async def create_stream_async(client: AsyncAnthropic) -> "AsyncStream[BetaRawMessageStreamEvent]": return await client.beta.messages.create( model="claude-fable-5", max_tokens=1024, messages=[{"role": "user", "content": "Hey claudius! Can you tell me what a solar eclipse is?"}], stream=True, ) # --- happy path ----------------------------------------------------------- class TestShapeBContinuation: @pytest.mark.respx(base_url=base_url) def test_splices_the_fallback_onto_the_refused_stream(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[sse_response(STREAM_A), sse_response(STREAM_B)]) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware(FALLBACKS)]) events = collect(create_stream(client)) # A's thinking + text are forwarded, a fallback boundary block is emitted # at the next monotonic index, then B's blocks continue after it. assert block_starts(events) == [ (0, "thinking"), (1, "text"), (2, "fallback"), (3, "text"), ] # The fallback block carries the from/to model transition. fallback = next( e.content_block for e in events if e.type == "content_block_start" and e.content_block.type == "fallback" ) assert fallback.from_.model == "claude-fable-5" assert fallback.to.model == FALLBACK_MODEL # Exactly one message_start (A's) and one message_stop reach the client — # B's message_start is suppressed. assert len([e for e in events if e.type == "message_start"]) == 1 assert len([e for e in events if e.type == "message_stop"]) == 1 assert len([e for e in events if e.type == "message_delta"]) == 1 @pytest.mark.respx(base_url=base_url) def test_usage_iterations_is_the_two_entry_server_shape(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[sse_response(STREAM_A), sse_response(STREAM_B)]) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware(FALLBACKS)]) events = collect(create_stream(client)) message_delta = next(e for e in events if e.type == "message_delta") assert message_delta.delta.stop_reason == "end_turn" iterations = message_delta.usage.iterations or [] # the 2-entry server shape, with no spurious `message: None` entry assert [(i.type, i.model) for i in iterations] == [ # type: ignore[union-attr] ("message", "claude-fable-5"), ("fallback_message", FALLBACK_MODEL), ] @pytest.mark.respx(base_url=base_url) def test_builds_request_b_as_a_shape_b_continuation(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[sse_response(STREAM_A), sse_response(STREAM_B)]) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware(FALLBACKS)]) collect(create_stream(client)) bodies = request_bodies(respx_mock) assert len(bodies) == 2 body_b = bodies[1] # Model swapped to the fallback, credit token from A's stop_details set. assert body_b["model"] == FALLBACK_MODEL credit = cast("dict[str, Any]", body_b["fallback_credit_token"]) assert credit["mode"] == "best_effort" assert len(credit["token"]) > 0 # Mutually exclusive with server-side fallback — both spellings absent. assert "fallback" not in body_b assert "fallbacks" not in body_b # max_tokens untouched (any render-shaping change would 400). assert body_b["max_tokens"] == 1024 # Original turn preserved; one assistant turn appended carrying the # [thinking, text] partial output as-is — the prefill claim authorizes # it verbatim, so no client-side filtering or trimming. assert len(body_b["messages"]) == 2 assert body_b["messages"][0] == PARAMS["messages"][0] appended = body_b["messages"][1] assert appended["role"] == "assistant" assert [block["type"] for block in appended["content"]] == ["thinking", "text"] assert "signature" in appended["content"][0] @pytest.mark.respx(base_url=base_url) def test_appends_the_fallback_credit_beta_to_both_the_original_and_hop_requests( self, respx_mock: MockRouter ) -> None: respx_mock.post("/v1/messages").mock(side_effect=[sse_response(STREAM_A), sse_response(STREAM_B)]) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware(FALLBACKS)]) # the request already carries a beta header; the default is appended to it. collect(create_stream(client, betas=["interleaved-thinking-2025-05-14"])) assert beta_headers(respx_mock) == [ "interleaved-thinking-2025-05-14, fallback-credit-2026-07-01", "interleaved-thinking-2025-05-14, fallback-credit-2026-07-01", ] @pytest.mark.respx(base_url=base_url) def test_the_betas_option_replaces_the_default_on_every_request(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[sse_response(STREAM_A), sse_response(STREAM_B)]) client = make_sync_client( middleware=[BetaRefusalFallbackMiddleware(FALLBACKS, betas=["fallback-credit-2027-01-01"])] ) collect(create_stream(client, betas=["fallback-credit-2026-06-01"])) assert beta_headers(respx_mock) == [ "fallback-credit-2026-06-01, fallback-credit-2027-01-01", "fallback-credit-2026-06-01, fallback-credit-2027-01-01", ] # --- edge cases ----------------------------------------------------------- class TestEdgeCases: @pytest.mark.respx(base_url=base_url) def test_a_refusal_without_a_prefill_claim_falls_back_to_shape_a(self, respx_mock: MockRouter) -> None: # fallback_has_prefill_claim: false — the partial output may not be # resent, so the middleware omits the prefill and resends the original # body with just the token attached. no_claim = "".join( [ message_start(), ev( { "type": "content_block_start", "index": 0, "content_block": {"type": "thinking", "thinking": "", "signature": ""}, } ), ev( { "type": "content_block_delta", "index": 0, "delta": {"type": "thinking_delta", "thinking": "considering the request"}, } ), ev( { "type": "content_block_delta", "index": 0, "delta": {"type": "signature_delta", "signature": "sig=="}, } ), ev({"type": "content_block_stop", "index": 0}), refusal_delta("tok_abc", False), ev({"type": "message_stop"}), ] ) respx_mock.post("/v1/messages").mock(side_effect=[sse_response(no_claim), sse_response(STREAM_B)]) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware(FALLBACKS)]) collect(create_stream(client)) bodies = request_bodies(respx_mock) assert bodies[1]["fallback_credit_token"] == redeemed_token("tok_abc") # No appended assistant turn — identical body (shape-A). assert bodies[1]["messages"] == PARAMS["messages"] @pytest.mark.respx(base_url=base_url) def test_refusal_with_no_credit_token_passes_a_through_and_logs_an_error( self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture ) -> None: no_token = re.sub(r'"fallback_credit_token":"[^"]*"', '"fallback_credit_token":null', STREAM_A) respx_mock.post("/v1/messages").mock(side_effect=[sse_response(no_token)]) client = make_lenient_client(middleware=[BetaRefusalFallbackMiddleware(FALLBACKS)]) events = collect(create_stream(client)) # Only the original request was made — no fallback. assert len(respx_mock.calls) == 1 errors = error_logs(caplog) assert len(errors) == 1 assert "no fallback_credit_token" in errors[0] # A passes through unchanged, ending in its own refusal (no fallback block). assert not any(e.type == "content_block_start" and e.content_block.type == "fallback" for e in events) assert next(e for e in events if e.type == "message_delta").delta.stop_reason == "refusal" @pytest.mark.respx(base_url=base_url) def test_a_400_on_the_prefill_form_retries_the_same_hop_without_the_partial( self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture ) -> None: respx_mock.post("/v1/messages").mock( side_effect=[ sse_response(STREAM_A), error_response("bad prefill", 400), sse_response(STREAM_B), ] ) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware(FALLBACKS)]) events = collect(create_stream(client)) # Attempt 1 appends A's partial; the 400 drops it and attempt 2 redeems # the same token against the unchanged body. bodies = request_bodies(respx_mock) assert len(bodies) == 3 assert len(bodies[1]["messages"]) == 2 assert bodies[2]["model"] == FALLBACK_MODEL assert bodies[2]["fallback_credit_token"] == bodies[1]["fallback_credit_token"] assert bodies[2]["messages"] == PARAMS["messages"] # The recovered hop is not a failure: one boundary, a normal completion. assert error_logs(caplog) == [] boundaries = [e for e in events if e.type == "content_block_start" and e.content_block.type == "fallback"] assert len(boundaries) == 1 assert next(e for e in events if e.type == "message_delta").delta.stop_reason == "end_turn" @pytest.mark.respx(base_url=base_url) def test_a_failed_fallback_request_replays_the_refusal_and_logs_an_error( self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture ) -> None: respx_mock.post("/v1/messages").mock( side_effect=[ sse_response(STREAM_A), # the prefill form 400s, the same-body retry 400s too — only # then does the hop count as failed error_response("nope", 400), error_response("nope", 400), ] ) client = make_lenient_client(middleware=[BetaRefusalFallbackMiddleware(FALLBACKS)]) events = collect(create_stream(client)) assert len(respx_mock.calls) == 3 errors = error_logs(caplog) assert len(errors) == 1 assert "HTTP 400" in errors[0] assert FALLBACK_MODEL in errors[0] # The failed hop was never reached, so it leaves no seam; A's suppressed # refusal is replayed — its credit token intact for a manual retry, with # no model recommendation (the failure was not a capacity error) — then # message_stop closes the stream. assert not any(e.type == "content_block_start" and e.content_block.type == "fallback" for e in events) delta = next(e for e in events if e.type == "message_delta") assert delta.delta.stop_reason == "refusal" assert delta.delta.stop_details is not None assert delta.delta.stop_details.fallback_credit_token is not None assert delta.delta.stop_details.recommended_model is None assert [(i.type, i.model) for i in (delta.usage.iterations or [])] == [ # type: ignore[union-attr] ("message", "claude-fable-5"), ] assert events[-1].type == "message_stop" @pytest.mark.respx(base_url=base_url) def test_a_capacity_failed_fallback_request_stamps_the_recommended_model( self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture ) -> None: respx_mock.post("/v1/messages").mock( side_effect=[ sse_response(STREAM_A), json_response({"type": "error", "error": {"type": "overloaded_error", "message": "later"}}, 529), ] ) client = make_lenient_client(middleware=[BetaRefusalFallbackMiddleware(FALLBACKS)]) events = collect(create_stream(client)) errors = error_logs(caplog) assert len(errors) == 1 assert "HTTP 529" in errors[0] # a capacity failure (429/529) stamps the failed model as the recommendation delta = next(e for e in events if e.type == "message_delta") assert delta.delta.stop_details is not None assert delta.delta.stop_details.recommended_model == FALLBACK_MODEL @pytest.mark.respx(base_url=base_url) def test_a_fallback_request_that_raises_replays_the_refusal( self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture ) -> None: respx_mock.post("/v1/messages").mock( side_effect=[sse_response(STREAM_A), httpx.ConnectError("connection reset")] ) client = make_lenient_client(middleware=[BetaRefusalFallbackMiddleware(FALLBACKS)]) events = collect(create_stream(client)) errors = error_logs(caplog) assert len(errors) == 1 # the raised error reaches the log as the SDK's connection error assert "Connection error." in errors[0] assert FALLBACK_MODEL in errors[0] # The stream still closes cleanly: A's refusal is replayed and # message_stop follows — not a hard stream error. assert next(e for e in events if e.type == "message_delta").delta.stop_reason == "refusal" assert events[-1].type == "message_stop" @pytest.mark.respx(base_url=base_url) def test_pass_through_preserves_sse_fields_beyond_event_and_data(self, respx_mock: MockRouter) -> None: wire = ( "retry: 1500\nevent: message_start\ndata: " + json.dumps( { "type": "message_start", "message": { "id": "msg_a", "type": "message", "role": "assistant", "model": "claude-fable-5", "content": [], "stop_reason": None, "stop_sequence": None, "usage": {"input_tokens": 10, "output_tokens": 1}, }, } ) + "\n\n" + ": keep-alive\nid: 42\nevent: message_delta\ndata: " + json.dumps( { "type": "message_delta", "delta": {"stop_reason": "end_turn", "stop_sequence": None}, "usage": {"output_tokens": 3}, } ) + "\n\n" + "event: message_stop\ndata: " + json.dumps({"type": "message_stop"}) + "\n\n" ) respx_mock.post("/v1/messages").mock(side_effect=[sse_response(wire)]) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware(FALLBACKS)]) stream = create_stream(client) raw = stream.response.read() assert raw.decode("utf-8") == wire @pytest.mark.respx(base_url=base_url) def test_a_non_refusal_stream_is_passed_through_untouched( self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture ) -> None: normal = "".join( [ message_start(), ev({"type": "content_block_start", "index": 0, "content_block": {"type": "text", "text": ""}}), ev( { "type": "content_block_delta", "index": 0, "delta": {"type": "text_delta", "text": "Sure!"}, } ), ev({"type": "content_block_stop", "index": 0}), ev( { "type": "message_delta", "delta": {"stop_reason": "end_turn", "stop_sequence": None}, "usage": {"output_tokens": 3}, } ), ev({"type": "message_stop"}), ] ) respx_mock.post("/v1/messages").mock(side_effect=[sse_response(normal)]) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware(FALLBACKS)]) events = collect(create_stream(client)) assert len(respx_mock.calls) == 1 assert error_logs(caplog) == [] assert skeleton(events) == [ "message_start", "start[0] text", "delta[0] text_delta", "stop[0]", "message_delta end_turn iter=[]", "message_stop", ] @pytest.mark.respx(base_url=base_url, assert_all_called=False) def test_server_side_fallbacks_raise_an_error(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[sse_response(STREAM_A)]) client = make_lenient_client(middleware=[BetaRefusalFallbackMiddleware(FALLBACKS)]) with pytest.raises( AnthropicError, match=r"Sending the `fallbacks:` request param is not supported when using the `BetaRefusalFallbackMiddleware`\. You should either remove the middleware and send `fallbacks:` with the `server-side-fallback-2026-07-01` beta header to let the API handle refusal fallbacks, or omit the `fallbacks:` param if you'd like `BetaRefusalFallbackMiddleware` to handle fallbacks on the client side\." ): client.beta.messages.create( model="claude-fable-5", max_tokens=1024, messages=[{"role": "user", "content": "Hey claudius! Can you tell me what a solar eclipse is?"}], fallbacks=[{"model": "server-fallback"}], stream=True, ) # the error is raised before any request is sent assert len(respx_mock.calls) == 0 # --- fallback state pinning ------------------------------------------------- class TestFallbackState: @pytest.mark.respx(base_url=base_url) def test_pins_the_state_to_the_hop_that_served(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[sse_response(STREAM_A), sse_response(STREAM_B)]) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware(FALLBACKS)]) state = BetaFallbackState() with state: collect(create_stream(client)) assert state.index == 0 @pytest.mark.respx(base_url=base_url) def test_a_pinned_state_starts_on_the_pinned_entry_and_chains_past_it(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[sse_response(STREAM_A), sse_response(STREAM_B)]) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware(TWO_FALLBACKS)]) state = BetaFallbackState() state.index = 0 with state: collect(create_stream(client)) bodies = request_bodies(respx_mock) assert len(bodies) == 2 # The initial request already carries the pinned entry's params; the # mid-stream refusal then chains to the entry after the pin. assert bodies[0]["model"] == FALLBACK_MODEL assert bodies[1]["model"] == SECOND_MODEL assert state.index == 1 @pytest.mark.respx(base_url=base_url) def test_warns_once_when_falling_back_without_a_state( self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture ) -> None: respx_mock.post("/v1/messages").mock( side_effect=[ sse_response(STREAM_A), sse_response(STREAM_B), sse_response(STREAM_A), sse_response(STREAM_B), ] ) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware(FALLBACKS)]) with caplog.at_level(logging.WARNING, logger=LOGGER_NAME): # drain the spliced stream so the fallback actually fires collect(create_stream(client)) collect(create_stream(client)) warnings = [record for record in caplog.records if record.name == LOGGER_NAME] assert len(warnings) == 1 assert "BetaFallbackState" in warnings[0].getMessage() # --- fallback chain --------------------------------------------------------- def hop_refusal(token: str | None = "tok_b", has_prefill_claim: bool = True) -> str: """A fallback hop that contributes one text block, then refuses with a fresh token.""" return "".join( [ message_start(), ev({"type": "content_block_start", "index": 0, "content_block": {"type": "text", "text": ""}}), ev( { "type": "content_block_delta", "index": 0, "delta": {"type": "text_delta", "text": "Partial from B. "}, } ), ev({"type": "content_block_stop", "index": 0}), refusal_delta(token, has_prefill_claim), ev({"type": "message_stop"}), ] ) class TestFallbackChain: @pytest.mark.respx(base_url=base_url) def test_a_refused_hop_splices_its_partial_and_chains_to_the_next_entry(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( side_effect=[sse_response(STREAM_A), sse_response(hop_refusal()), sse_response(STREAM_B)] ) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware(TWO_FALLBACKS)]) events = collect(create_stream(client)) bodies = request_bodies(respx_mock) assert len(bodies) == 3 # Hop 1 redeems A's token; hop 2 redeems the fresh token minted by hop 1's # refusal, with hop 1's partial extending the same turn as-is. assert bodies[1]["model"] == FALLBACK_MODEL assert bodies[2]["model"] == SECOND_MODEL assert bodies[2]["fallback_credit_token"] == redeemed_token("tok_b") assert bodies[2]["fallback_credit_token"] != bodies[1]["fallback_credit_token"] assert bodies[2]["messages"][1]["content"] == [ *bodies[1]["messages"][1]["content"], {"type": "text", "text": "Partial from B. "}, ] # One continuous message: A's blocks, boundary, hop 1's partial, boundary, # hop 2's blocks — indices stay monotonic across all three streams. assert block_starts(events) == [ (0, "thinking"), (1, "text"), (2, "fallback"), (3, "text"), (4, "fallback"), (5, "text"), ] boundaries = [ e.content_block for e in events if e.type == "content_block_start" and e.content_block.type == "fallback" ] assert (boundaries[0].from_.model, boundaries[0].to.model) == ("claude-fable-5", FALLBACK_MODEL) assert (boundaries[1].from_.model, boundaries[1].to.model) == (FALLBACK_MODEL, SECOND_MODEL) # Hop 1's refusal delta is suppressed; the terminal delta carries every hop. deltas = [e for e in events if e.type == "message_delta"] assert len(deltas) == 1 assert deltas[0].delta.stop_reason == "end_turn" assert [(i.type, i.model) for i in (deltas[0].usage.iterations or [])] == [ # type: ignore[union-attr] ("message", "claude-fable-5"), ("message", FALLBACK_MODEL), ("fallback_message", SECOND_MODEL), ] assert len([e for e in events if e.type == "message_stop"]) == 1 @pytest.mark.respx(base_url=base_url) def test_a_refused_hop_without_a_prefill_claim_drops_its_partial_from_the_next_request( self, respx_mock: MockRouter ) -> None: respx_mock.post("/v1/messages").mock( side_effect=[ sse_response(STREAM_A), sse_response(hop_refusal("tok_b", False)), sse_response(STREAM_B), ] ) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware(TWO_FALLBACKS)]) collect(create_stream(client)) bodies = request_bodies(respx_mock) assert len(bodies) == 3 assert bodies[2]["fallback_credit_token"] == redeemed_token("tok_b") # Hop 2 redeems the fresh token against the body it was minted for — # hop 1's request, including its appended turn — without hop 1's own # (unclaimed) partial output. assert bodies[2]["messages"] == bodies[1]["messages"] @pytest.mark.respx(base_url=base_url) def test_an_http_failed_hop_is_skipped_and_the_unredeemed_token_carries_to_the_next_entry( self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture ) -> None: respx_mock.post("/v1/messages").mock( side_effect=[ sse_response(STREAM_A), json_response({"type": "error", "error": {"type": "overloaded_error", "message": "later"}}, 529), sse_response(STREAM_B), ] ) client = make_sync_client( middleware=[BetaRefusalFallbackMiddleware([{"model": FALLBACK_MODEL}, {"model": SECOND_MODEL}])] ) events = collect(create_stream(client)) errors = error_logs(caplog) assert len(errors) == 1 assert "HTTP 529" in errors[0] assert FALLBACK_MODEL in errors[0] assert len(respx_mock.calls) == 3 # Same token and continuation — the failed hop never redeemed them. bodies = request_bodies(respx_mock) assert bodies[2]["model"] == SECOND_MODEL assert bodies[2]["fallback_credit_token"] == bodies[1]["fallback_credit_token"] assert bodies[2]["messages"] == bodies[1]["messages"] # The failed hop was never reached: no seam for it — one boundary, from # A straight to the entry that served. boundaries = [ e.content_block for e in events if e.type == "content_block_start" and e.content_block.type == "fallback" ] assert [(b.from_.model, b.to.model) for b in boundaries] == [ ("claude-fable-5", SECOND_MODEL), ] # The failed hop is absent from iterations (no usage came back). delta = next(e for e in events if e.type == "message_delta") assert delta.delta.stop_reason == "end_turn" assert [(i.type, i.model) for i in (delta.usage.iterations or [])] == [ # type: ignore[union-attr] ("message", "claude-fable-5"), ("fallback_message", SECOND_MODEL), ] @pytest.mark.respx(base_url=base_url) def test_a_terminal_refusal_with_no_entries_left_is_emitted_with_the_full_iteration_chain( self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture ) -> None: respx_mock.post("/v1/messages").mock(side_effect=[sse_response(STREAM_A), sse_response(hop_refusal())]) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware(FALLBACKS)]) events = collect(create_stream(client)) assert len(respx_mock.calls) == 2 errors = error_logs(caplog) assert len(errors) == 1 assert "no fallback entries remain" in errors[0] delta = next(e for e in events if e.type == "message_delta") assert delta.delta.stop_reason == "refusal" # The fresh token reaches the client for a manual retry. assert delta.delta.stop_details is not None assert delta.delta.stop_details.fallback_credit_token == "tok_b" assert [(i.type, i.model) for i in (delta.usage.iterations or [])] == [ # type: ignore[union-attr] ("message", "claude-fable-5"), ("fallback_message", FALLBACK_MODEL), ] @pytest.mark.respx(base_url=base_url) def test_a_token_less_refusal_on_the_final_hop_is_still_logged( self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture ) -> None: respx_mock.post("/v1/messages").mock(side_effect=[sse_response(STREAM_A), sse_response(hop_refusal(None))]) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware(FALLBACKS)]) events = collect(create_stream(client)) errors = error_logs(caplog) assert len(errors) == 1 assert "no fallback_credit_token" in errors[0] assert next(e for e in events if e.type == "message_delta").delta.stop_reason == "refusal" def test_an_empty_chain_passes_the_stream_through_untouched(self, caplog: pytest.LogCaptureFixture) -> None: middleware = BetaRefusalFallbackMiddleware([]) client = make_sync_client() options = FinalRequestOptions.construct( method="post", url="/v1/messages?beta=true", json_data=dict(PARAMS), headers={} ) request = APIRequest( options=options, cast_to=BetaMessage, stream=True, stream_cls=Stream[BetaRawMessageStreamEvent], ) response = APIResponse( raw=sse_response(STREAM_A), cast_to=BetaMessage, client=client, stream=True, stream_cls=Stream[BetaRawMessageStreamEvent], options=options, ) calls: list[APIRequest] = [] def call_next(req: APIRequest) -> APIResponse[Any]: calls.append(req) return response out = middleware.handle(request, call_next) # With nothing to hop to, the response isn't even wrapped — no per-event # decode/re-encode overhead, and no error: this is the steady state of an # exhausted or fully-pinned chain. assert out is response assert len(calls) == 1 assert error_logs(caplog) == [] @pytest.mark.respx(base_url=base_url) def test_a_hop_whose_request_raises_is_skipped_and_the_unredeemed_token_carries( self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture ) -> None: respx_mock.post("/v1/messages").mock( side_effect=[ sse_response(STREAM_A), httpx.ConnectError("connection reset"), sse_response(STREAM_B), ] ) client = make_sync_client( middleware=[BetaRefusalFallbackMiddleware([{"model": FALLBACK_MODEL}, {"model": SECOND_MODEL}])] ) events = collect(create_stream(client)) errors = error_logs(caplog) assert len(errors) == 1 assert "Connection error." in errors[0] assert FALLBACK_MODEL in errors[0] assert len(respx_mock.calls) == 3 # Same token — the raising hop never redeemed it. bodies = request_bodies(respx_mock) assert bodies[2]["model"] == SECOND_MODEL assert bodies[2]["fallback_credit_token"] == bodies[1]["fallback_credit_token"] # The stream completes normally from the next entry. delta = next(e for e in events if e.type == "message_delta") assert delta.delta.stop_reason == "end_turn" assert [(i.type, i.model) for i in (delta.usage.iterations or [])] == [ # type: ignore[union-attr] ("message", "claude-fable-5"), ("fallback_message", SECOND_MODEL), ] # --- pre-stream refusals ------------------------------------------------------ def serving_stream(model: str = FALLBACK_MODEL, message_id: str = "msg_b") -> str: return "".join( [ ev( { "type": "message_start", "message": { "id": message_id, "type": "message", "role": "assistant", "model": model, "content": [], "stop_reason": None, "stop_sequence": None, "usage": {"input_tokens": 12, "output_tokens": 1}, }, } ), ev({"type": "content_block_start", "index": 0, "content_block": {"type": "text", "text": ""}}), ev({"type": "content_block_delta", "index": 0, "delta": {"type": "text_delta", "text": "Happy to help."}}), ev({"type": "content_block_stop", "index": 0}), ev( { "type": "message_delta", "delta": {"stop_reason": "end_turn", "stop_sequence": None}, "usage": {"output_tokens": 9}, } ), ev({"type": "message_stop"}), ] ) class TestPreStreamRefusals: """A refusal that arrives before any output streamed: the retry is free and invisible, so it fires even without a credit token, and the serving hop's message_start opens the wire carrying the primary's message id.""" pre_stream_refusal = "".join([message_start(), refusal_delta("tok_abc", False), ev({"type": "message_stop"})]) @pytest.mark.respx(base_url=base_url) def test_the_serving_start_opens_the_wire_with_the_primary_message_id(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( side_effect=[sse_response(self.pre_stream_refusal), sse_response(serving_stream())] ) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware(FALLBACKS)]) events = collect(create_stream(client)) # The refused hop's message_start never reaches the client; the serving # hop's opens the wire, rewritten to the primary's message id. starts = [e for e in events if e.type == "message_start"] assert len(starts) == 1 assert starts[0].message.model == FALLBACK_MODEL assert starts[0].message.id == "msg_a" # One seam at index 0, then the serving hop's content after it. assert block_starts(events) == [(0, "fallback"), (1, "text")] delta = next(e for e in events if e.type == "message_delta") assert delta.delta.stop_reason == "end_turn" assert [(i.type, i.model) for i in (delta.usage.iterations or [])] == [ # type: ignore[union-attr] ("message", "claude-fable-5"), ("fallback_message", FALLBACK_MODEL), ] @pytest.mark.respx(base_url=base_url) def test_a_token_less_pre_stream_refusal_still_retries( self, respx_mock: MockRouter, caplog: pytest.LogCaptureFixture ) -> None: token_less = "".join([message_start(), refusal_delta(None), ev({"type": "message_stop"})]) respx_mock.post("/v1/messages").mock(side_effect=[sse_response(token_less), sse_response(serving_stream())]) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware(FALLBACKS)]) events = collect(create_stream(client)) # nothing had streamed, so the retry fired despite the missing token assert error_logs(caplog) == [] bodies = request_bodies(respx_mock) assert len(bodies) == 2 assert bodies[1]["model"] == FALLBACK_MODEL assert "fallback_credit_token" not in bodies[1] assert next(e for e in events if e.type == "message_delta").delta.stop_reason == "end_turn" @pytest.mark.respx(base_url=base_url) def test_a_chain_of_pre_stream_declines_queues_every_seam_in_order(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( side_effect=[ sse_response(self.pre_stream_refusal), sse_response("".join([message_start(), refusal_delta("tok_b"), ev({"type": "message_stop"})])), sse_response(serving_stream(SECOND_MODEL)), ] ) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware(TWO_FALLBACKS)]) events = collect(create_stream(client)) # serving start first, then both seams, then the serving content assert events[0].type == "message_start" assert events[0].message.id == "msg_a" starts = [(e.index, e.content_block) for e in events if e.type == "content_block_start"] assert [(i, b.type) for i, b in starts] == [(0, "fallback"), (1, "fallback"), (2, "text")] seam_one, seam_two = starts[0][1], starts[1][1] assert seam_one.type == "fallback" and seam_two.type == "fallback" assert (seam_one.from_.model, seam_one.to.model) == ("claude-fable-5", FALLBACK_MODEL) assert (seam_two.from_.model, seam_two.to.model) == (FALLBACK_MODEL, SECOND_MODEL) delta = next(e for e in events if e.type == "message_delta") assert [(i.type, i.model) for i in (delta.usage.iterations or [])] == [ # type: ignore[union-attr] ("message", "claude-fable-5"), ("message", FALLBACK_MODEL), ("fallback_message", SECOND_MODEL), ] # --- history seam replay ------------------------------------------------------ class TestHistorySeamReplay: """Pinning is explicit-state-only: a `fallback` seam block replayed in the request history never pins — without a `BetaFallbackState` the first request goes to the original model. The seam blocks themselves are this middleware's client-side markers, so they are filtered out of the wire request, and an assistant turn that was only a seam is dropped whole — `content: []` is an invalid body.""" @pytest.mark.respx(base_url=base_url) def test_a_history_seam_without_a_state_does_not_pin(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[sse_response(serving_stream("claude-fable-5"))]) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware(TWO_FALLBACKS)]) stream = client.beta.messages.create( model="claude-fable-5", max_tokens=1024, messages=[ {"role": "user", "content": "hello"}, { "role": "assistant", "content": [ { "type": "fallback", "from": {"model": "claude-fable-5"}, "to": {"model": FALLBACK_MODEL}, }, {"type": "text", "text": "earlier turn"}, ], }, {"role": "user", "content": "and again?"}, ], stream=True, ) collect(stream) bodies = request_bodies(respx_mock) assert len(bodies) == 1 # no state, no pin — the first request goes to the original model assert bodies[0]["model"] == "claude-fable-5" # the seam block is the middleware's own marker — filtered off the wire assert bodies[0]["messages"][1]["content"] == [{"type": "text", "text": "earlier turn"}] assert beta_headers(respx_mock) == ["fallback-credit-2026-07-01"] @pytest.mark.respx(base_url=base_url) def test_a_seam_only_assistant_turn_is_dropped_whole(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[sse_response(serving_stream("claude-fable-5"))]) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware(FALLBACKS)]) stream = client.beta.messages.create( model="claude-fable-5", max_tokens=1024, messages=[ {"role": "user", "content": "hello"}, { "role": "assistant", "content": [ { "type": "fallback", "from": {"model": "claude-fable-5"}, "to": {"model": FALLBACK_MODEL}, } ], }, {"role": "user", "content": "and again?"}, ], stream=True, ) collect(stream) bodies = request_bodies(respx_mock) assert len(bodies) == 1 # stripping left the assistant turn empty, so the turn is omitted — # not sent as `content: []`, which the server rejects assert bodies[0]["messages"] == [ {"role": "user", "content": "hello"}, {"role": "user", "content": "and again?"}, ] # the rest of the request is intact assert bodies[0]["model"] == "claude-fable-5" assert bodies[0]["max_tokens"] == 1024 assert beta_headers(respx_mock) == ["fallback-credit-2026-07-01"] # --- per-hop overrides ---------------------------------------------------------- class TestPerHopOverrides: @pytest.mark.respx(base_url=base_url) def test_entry_overrides_apply_to_the_hop_request(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[sse_response(STREAM_A), sse_response(STREAM_B)]) client = make_sync_client( middleware=[BetaRefusalFallbackMiddleware([{"model": FALLBACK_MODEL, "max_tokens": 32}])] ) collect(create_stream(client)) bodies = request_bodies(respx_mock) assert bodies[0]["max_tokens"] == 1024 # the entry's overrides are merged over the hop's body, applied to the # serving hop only assert bodies[1]["max_tokens"] == 32 assert bodies[1]["model"] == FALLBACK_MODEL # --- cancellation ----------------------------------------------------------- class TestCancellation: @pytest.mark.respx(base_url=base_url) def test_closing_the_stream_mid_passthrough_tears_down_without_a_fallback_request( self, respx_mock: MockRouter ) -> None: respx_mock.post("/v1/messages").mock(side_effect=[sse_response(STREAM_A)]) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware(FALLBACKS)]) stream = create_stream(client) seen = 0 for _event in stream: seen += 1 if seen == 2: break stream.close() # The splice never reached A's refusal, so no hop request was issued and # teardown released the underlying response without error. assert len(respx_mock.calls) == 1 # --- tool-use refusals ---------------------------------------------------- # # Synthetic SSE (web_search-shaped) built from the documented wire shapes: # server_tool_use streams its input via input_json_delta after an empty # `input:{}`, and *_tool_result blocks arrive as a single content_block_start # with full content (no deltas). The server decides prefillability and # signals it via `fallback_has_prefill_claim`; the client's only rewrite is # reassembling tool inputs from their accumulated JSON deltas. TOOL_USE_ID = "srvtoolu_01" class TestToolUseRefusals: @pytest.mark.respx(base_url=base_url) def test_refusal_after_a_completed_server_tool(self, respx_mock: MockRouter) -> None: stream_a = "".join( [ message_start(), ev( { "type": "content_block_start", "index": 0, "content_block": {"type": "thinking", "thinking": "", "signature": ""}, } ), ev( { "type": "content_block_delta", "index": 0, "delta": {"type": "thinking_delta", "thinking": "let me look this up"}, } ), ev( { "type": "content_block_delta", "index": 0, "delta": {"type": "signature_delta", "signature": "sig=="}, } ), ev({"type": "content_block_stop", "index": 0}), # server_tool_use: real input arrives via input_json_delta, not content_block_start. ev( { "type": "content_block_start", "index": 1, "content_block": { "type": "server_tool_use", "id": TOOL_USE_ID, "name": "web_search", "input": {}, }, } ), ev( { "type": "content_block_delta", "index": 1, "delta": {"type": "input_json_delta", "partial_json": '{"query":"solar eclipse"}'}, } ), ev({"type": "content_block_stop", "index": 1}), # web_search_tool_result: full content in the start frame, no deltas. ev( { "type": "content_block_start", "index": 2, "content_block": { "type": "web_search_tool_result", "tool_use_id": TOOL_USE_ID, "content": [ { "type": "web_search_result", "url": "https://example.com", "title": "x", "encrypted_content": "e", "page_age": None, } ], }, } ), ev({"type": "content_block_stop", "index": 2}), ev({"type": "content_block_start", "index": 3, "content_block": {"type": "text", "text": ""}}), ev( { "type": "content_block_delta", "index": 3, "delta": {"type": "text_delta", "text": "Based on that, "}, } ), ev({"type": "content_block_stop", "index": 3}), refusal_delta(), ev({"type": "message_stop"}), ] ) respx_mock.post("/v1/messages").mock(side_effect=[sse_response(stream_a), sse_response(STREAM_B)]) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware(FALLBACKS)]) events = collect(create_stream(client)) # A's four blocks forwarded, fallback boundary at index 4, B continues at 5. assert block_starts(events) == [ (0, "thinking"), (1, "server_tool_use"), (2, "web_search_tool_result"), (3, "text"), (4, "fallback"), (5, "text"), ] appended = request_bodies(respx_mock)[1]["messages"][1] assert appended["role"] == "assistant" assert [block["type"] for block in appended["content"]] == [ "thinking", "server_tool_use", "web_search_tool_result", "text", ] # The tool input is the parsed input_json_delta payload, not the empty # `{}` from content_block_start. assert appended["content"][1] == { "type": "server_tool_use", "id": TOOL_USE_ID, "name": "web_search", "input": {"query": "solar eclipse"}, } # The result keeps its pairing id and content. assert appended["content"][2]["tool_use_id"] == TOOL_USE_ID @pytest.mark.respx(base_url=base_url) def test_full_fixture_tool_wire(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[sse_response(STREAM_A_TOOL), sse_response(STREAM_B)]) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware(FALLBACKS)]) events = collect(create_stream(client)) assert block_starts(events) == [ (0, "server_tool_use"), (1, "web_search_tool_result"), (2, "text"), (3, "fallback"), (4, "text"), ] appended = request_bodies(respx_mock)[1]["messages"][1] assert [block["type"] for block in appended["content"]] == [ "server_tool_use", "web_search_tool_result", "text", ] # Tool input reassembled from the accumulated input_json_delta chunks. assert appended["content"][0] == { "type": "server_tool_use", "id": "srvtoolu_fixture_a_0001", "name": "web_search", "input": {"query": "solar eclipse viewing safety news 2026"}, } # The result block keeps its pairing id. assert appended["content"][1]["tool_use_id"] == "srvtoolu_fixture_a_0001" assert appended["content"][1]["type"] == "web_search_tool_result" @pytest.mark.respx(base_url=base_url) def test_mid_loop_refusal_ending_in_thinking_strips_the_trailing_thinking_block( self, respx_mock: MockRouter ) -> None: # [server_tool_use, result, thinking] — the server granted a prefill # claim, but an assistant turn cannot end in a thinking block (the # server 400s it), so the trailing thinking is stripped from the # appended turn while everything before it is resent verbatim. stream_a = "".join( [ message_start(), ev( { "type": "content_block_start", "index": 0, "content_block": { "type": "server_tool_use", "id": TOOL_USE_ID, "name": "web_search", "input": {}, }, } ), ev( { "type": "content_block_delta", "index": 0, "delta": {"type": "input_json_delta", "partial_json": '{"query":"x"}'}, } ), ev({"type": "content_block_stop", "index": 0}), ev( { "type": "content_block_start", "index": 1, "content_block": { "type": "web_search_tool_result", "tool_use_id": TOOL_USE_ID, "content": [], }, } ), ev({"type": "content_block_stop", "index": 1}), ev( { "type": "content_block_start", "index": 2, "content_block": {"type": "thinking", "thinking": "", "signature": ""}, } ), ev( { "type": "content_block_delta", "index": 2, "delta": {"type": "thinking_delta", "thinking": "hmm"}, } ), ev( { "type": "content_block_delta", "index": 2, "delta": {"type": "signature_delta", "signature": "sig=="}, } ), ev({"type": "content_block_stop", "index": 2}), refusal_delta(), ev({"type": "message_stop"}), ] ) respx_mock.post("/v1/messages").mock(side_effect=[sse_response(stream_a), sse_response(STREAM_B)]) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware(FALLBACKS)]) collect(create_stream(client)) body_b = request_bodies(respx_mock)[1] assert body_b["fallback_credit_token"] == redeemed_token("tok_abc") appended = body_b["messages"][1] assert appended["role"] == "assistant" assert [block["type"] for block in appended["content"]] == [ "server_tool_use", "web_search_tool_result", ] @pytest.mark.respx(base_url=base_url) def test_a_partial_of_only_thinking_falls_back_to_the_same_body_form(self, respx_mock: MockRouter) -> None: # The refusal cut the stream while only a thinking block had streamed: # stripping the trailing thinking empties the continuation, so no # assistant turn is appended and the token is redeemed against the # same body. stream_a = "".join( [ message_start(), ev( { "type": "content_block_start", "index": 0, "content_block": {"type": "thinking", "thinking": "", "signature": ""}, } ), ev( { "type": "content_block_delta", "index": 0, "delta": {"type": "thinking_delta", "thinking": "hmm"}, } ), ev({"type": "content_block_stop", "index": 0}), refusal_delta(), ev({"type": "message_stop"}), ] ) respx_mock.post("/v1/messages").mock(side_effect=[sse_response(stream_a), sse_response(STREAM_B)]) client = make_sync_client(middleware=[BetaRefusalFallbackMiddleware(FALLBACKS)]) collect(create_stream(client)) body_b = request_bodies(respx_mock)[1] assert body_b["fallback_credit_token"] == redeemed_token("tok_abc") assert body_b["messages"] == PARAMS["messages"] # --- async ------------------------------------------------------------------ class TestAsyncSplicing: @pytest.mark.respx(base_url=base_url) async def test_splices_the_fallback_onto_the_refused_stream(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[sse_response(STREAM_A), sse_response(STREAM_B)]) client = make_async_client(middleware=[BetaRefusalFallbackMiddleware(FALLBACKS)]) state = BetaFallbackState() with state: stream = await create_stream_async(client) events: List[BetaRawMessageStreamEvent] = [event async for event in stream] assert block_starts(events) == [ (0, "thinking"), (1, "text"), (2, "fallback"), (3, "text"), ] assert len([e for e in events if e.type == "message_start"]) == 1 assert len([e for e in events if e.type == "message_stop"]) == 1 delta = next(e for e in events if e.type == "message_delta") assert delta.delta.stop_reason == "end_turn" assert [(i.type, i.model) for i in (delta.usage.iterations or [])] == [ # type: ignore[union-attr] ("message", "claude-fable-5"), ("fallback_message", FALLBACK_MODEL), ] assert state.index == 0 bodies = request_bodies(respx_mock) assert bodies[1]["model"] == FALLBACK_MODEL assert isinstance(bodies[1]["fallback_credit_token"], dict) assert bodies[1]["fallback_credit_token"]["mode"] == "best_effort" appended = bodies[1]["messages"][1] assert appended["role"] == "assistant" assert [block["type"] for block in appended["content"]] == ["thinking", "text"] @pytest.mark.respx(base_url=base_url) async def test_a_refused_hop_splices_its_partial_and_chains_to_the_next_entry(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( side_effect=[sse_response(STREAM_A), sse_response(hop_refusal()), sse_response(STREAM_B)] ) client = make_async_client(middleware=[BetaRefusalFallbackMiddleware(TWO_FALLBACKS)]) stream = await create_stream_async(client) events: List[BetaRawMessageStreamEvent] = [event async for event in stream] assert block_starts(events) == [ (0, "thinking"), (1, "text"), (2, "fallback"), (3, "text"), (4, "fallback"), (5, "text"), ] deltas = [e for e in events if e.type == "message_delta"] assert len(deltas) == 1 assert [(i.type, i.model) for i in (deltas[0].usage.iterations or [])] == [ # type: ignore[union-attr] ("message", "claude-fable-5"), ("message", FALLBACK_MODEL), ("fallback_message", SECOND_MODEL), ] bodies = request_bodies(respx_mock) assert bodies[2]["fallback_credit_token"] == redeemed_token("tok_b") @pytest.mark.respx(base_url=base_url) async def test_closing_the_stream_mid_passthrough_tears_down_without_a_fallback_request( self, respx_mock: MockRouter ) -> None: respx_mock.post("/v1/messages").mock(side_effect=[sse_response(STREAM_A)]) client = make_async_client(middleware=[BetaRefusalFallbackMiddleware(FALLBACKS)]) stream = await create_stream_async(client) seen = 0 async for _event in stream: seen += 1 if seen == 2: break await stream.close() assert len(respx_mock.calls) == 1 anthropic-sdk-python-0.120.2/tests/lib/test_scoped_client.py000066400000000000000000000161771523216435200241140ustar00rootroot00000000000000"""Direct unit tests for :func:`_copy_client_with_bearer_auth`. These verify the load-bearing invariants of the util — auth replaced, parent not mutated, helper-telemetry header set — without re-exercising ``copy()``'s own inheritance contract (which is the SDK's job to keep working). Both sync and async client paths are covered so the ``ClientT`` generic threads through. """ from __future__ import annotations import httpx import pytest from anthropic import Anthropic, AsyncAnthropic from anthropic.lib._scoped_client import _copy_client_with_bearer_auth def test_sets_bearer_auth_token_on_copy() -> None: parent = Anthropic(api_key="parent-key") scoped = _copy_client_with_bearer_auth(parent, auth_token="env-key", helper="environments-work-poller") assert scoped.auth_token == "env-key" def test_clears_parent_api_key_on_copy() -> None: """The post-hoc ``scoped.api_key = None`` mutation is the only thing keeping the parent's ``X-Api-Key`` off the sub-client's wire. If this ever starts returning a sub-client with ``api_key`` set, the parent's API credential would silently authenticate every helper request.""" parent = Anthropic(api_key="parent-key") scoped = _copy_client_with_bearer_auth(parent, auth_token="env-key", helper="environments-work-poller") assert scoped.api_key is None def test_clears_inherited_credentials_provider() -> None: """When the parent client carries a credentials provider (e.g. workload identity), the sub-client must use the explicit bearer token as the unambiguous credential — not stack the provider's auth on top of it.""" from anthropic.lib.credentials._types import AccessToken def fake_provider(*, force_refresh: bool = False) -> AccessToken: # noqa: ARG001 return AccessToken(token="provider-token") parent = Anthropic(api_key="parent-key", credentials=fake_provider) scoped = _copy_client_with_bearer_auth(parent, auth_token="env-key", helper="environments-work-poller") assert scoped.credentials is None def test_stamps_helper_telemetry_header() -> None: parent = Anthropic(api_key="parent-key") scoped = _copy_client_with_bearer_auth(parent, auth_token="env-key", helper="environments-worker") assert scoped._custom_headers.get("x-stainless-helper") == "environments-worker" def test_does_not_mutate_parent_client() -> None: """Building the sub-client must not touch the parent's auth state. Without this, a long-lived parent client could be silently re-credentialed every time a runner helper started.""" parent = Anthropic(api_key="parent-key") _copy_client_with_bearer_auth(parent, auth_token="env-key", helper="environments-work-poller") assert parent.api_key == "parent-key" assert parent.auth_token is None def test_empty_auth_token_raises() -> None: """An empty ``auth_token`` would silently fall back to the parent's ``auth_token`` via ``copy()``'s truthy-or, producing a sub-client with no intentional credential set.""" parent = Anthropic(api_key="parent-key") with pytest.raises(ValueError, match="auth_token"): _copy_client_with_bearer_auth(parent, auth_token="", helper="environments-work-poller") @pytest.mark.asyncio() async def test_async_client_path_clears_api_key_and_sets_bearer() -> None: """The same invariants hold for ``AsyncAnthropic`` — verifies the ``ClientT`` typevar threads sync/async correctly through ``copy()``.""" parent = AsyncAnthropic(api_key="parent-key") scoped = _copy_client_with_bearer_auth(parent, auth_token="env-key", helper="session-tool-runner") assert isinstance(scoped, AsyncAnthropic) assert scoped.api_key is None assert scoped.auth_token == "env-key" assert scoped._custom_headers.get("x-stainless-helper") == "session-tool-runner" def test_strips_inherited_authorization_from_parent_default_headers() -> None: """If the parent client was configured with a custom ``default_headers={"Authorization": ...}`` (or ``X-Api-Key``), those would otherwise win over the bearer we just set because :meth:`AsyncAnthropic.default_headers` merges ``_custom_headers`` after ``auth_headers``. The helper strips them from the sub-client's custom headers so the bearer is unambiguous on the wire.""" parent = Anthropic( api_key="parent-key", default_headers={"Authorization": "Bearer parent-token", "X-Api-Key": "parent-key"}, ) scoped = _copy_client_with_bearer_auth(parent, auth_token="env-key", helper="environments-worker") # Strip is case-insensitive, so neither name leaks through under any # casing. custom = {k.lower() for k in scoped._custom_headers} assert "authorization" not in custom assert "x-api-key" not in custom # Parent client is unmutated — its custom headers still carry the original # entries. parent_custom = {k.lower(): v for k, v in parent._custom_headers.items()} assert parent_custom["authorization"] == "Bearer parent-token" assert parent_custom["x-api-key"] == "parent-key" @pytest.mark.asyncio() async def test_scoped_sub_client_sends_only_bearer_on_the_wire() -> None: """Integration-level check: send a real HTTP request through the scoped sub-client (via ``httpx.MockTransport``) and inspect the headers actually on the wire. Asserts exactly one auth credential is sent — the bearer — and the parent's ``X-Api-Key`` doesn't leak. This is the surface that the case-mismatch bug fixed by this whole refactor lived on. The unit tests above check the sub-client's *state*; this one checks the request the SDK builds *from* that state. If a future change to ``_build_headers`` reverses the merge order or ``_copy_client_with_bearer_auth`` stops stripping the parent's ``X-Api-Key``, this is the test that catches it.""" captured: list[httpx.Request] = [] async def handler(req: httpx.Request) -> httpx.Response: captured.append(req) return httpx.Response(200, json={"id": "agent_test"}) transport = httpx.MockTransport(handler) http_client = httpx.AsyncClient(transport=transport) # Parent has *every* way a credential could leak: an api_key, an # Authorization in default_headers, AND an X-Api-Key in default_headers. parent = AsyncAnthropic( api_key="parent-key", default_headers={ "Authorization": "Bearer parent-token", "X-Api-Key": "parent-key", }, http_client=http_client, ) scoped = _copy_client_with_bearer_auth(parent, auth_token="env-key", helper="environments-worker") # Any GET that doesn't require a body — agents.retrieve is a thin GET that # exercises the same auth path the worker / poller use. await scoped.beta.agents.retrieve("agent_test") assert len(captured) == 1 req = captured[0] # ``httpx.Headers`` is case-insensitive, so .get() catches any casing. assert req.headers.get("authorization") == "Bearer env-key" # The parent's ``X-Api-Key`` must NOT be on the wire — that was the bug. assert req.headers.get("x-api-key") is None # Helper telemetry is on every scoped request. assert req.headers.get("x-stainless-helper") == "environments-worker" anthropic-sdk-python-0.120.2/tests/lib/test_stainless_helpers.py000066400000000000000000000203501523216435200250140ustar00rootroot00000000000000from __future__ import annotations from typing import cast import httpx import respx import pytest from anthropic import Anthropic, AsyncAnthropic, _compat from anthropic.types.beta import BetaToolParam from anthropic._base_client import _APPEND_HEADERS from anthropic.lib._stainless_helpers import ( STAINLESS_HELPER_HEADER, tag_helper, helper_header, ) from ..conftest import base_url class _TaggedDict(dict): # type: ignore[type-arg] """Plain dicts reject ``object.__setattr__`` — helpers tag attribute-capable subclasses.""" def test_helper_header() -> None: assert helper_header("BetaToolRunner") == {STAINLESS_HELPER_HEADER: "BetaToolRunner"} def test_helper_header_is_an_append_header() -> None: # ``merge_headers`` only appends keys it knows about — keep this aligned assert STAINLESS_HELPER_HEADER in _APPEND_HEADERS def _message_json() -> dict[str, object]: return { "id": "msg_abc123", "type": "message", "role": "assistant", "model": "claude-sonnet-4-5", "content": [{"type": "text", "text": "hi"}], "stop_reason": "end_turn", "stop_sequence": None, "usage": {"input_tokens": 1, "output_tokens": 1}, } @pytest.mark.respx(base_url=base_url) class TestSyncWireHeaders: def test_caller_tag_is_appended_not_clobbered(self, client: Anthropic, respx_mock: respx.MockRouter) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=_message_json())) tool = cast("BetaToolParam", _TaggedDict({"name": "t", "description": "d", "input_schema": {"type": "object"}})) tag_helper(tool, "mcp_tool") client.beta.messages.create( model="claude-sonnet-4-5", max_tokens=16, messages=[{"role": "user", "content": "hello"}], tools=[tool], extra_headers={"X-Stainless-Helper": "caller-tag"}, ) request = respx_mock.calls.last.request values = request.headers.get_list(STAINLESS_HELPER_HEADER) assert values == ["mcp_tool, caller-tag"] @pytest.mark.skipif(_compat.PYDANTIC_V1, reason="parse() response post-parser is pydantic-v2 only") def test_parse_sends_single_header_line(self, client: Anthropic, respx_mock: respx.MockRouter) -> None: # regression: the literal tag and the collected tags used to land under # two casings of the key, producing two header lines on the wire respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=_message_json())) client.beta.messages.parse( model="claude-sonnet-4-5", max_tokens=16, messages=[{"role": "user", "content": "hello"}], ) request = respx_mock.calls.last.request values = request.headers.get_list(STAINLESS_HELPER_HEADER) assert values == ["beta.messages.parse"] @pytest.mark.skipif(_compat.PYDANTIC_V1, reason="parse() response post-parser is pydantic-v2 only") def test_parse_merges_caller_extra_headers(self, client: Anthropic, respx_mock: respx.MockRouter) -> None: # caller-supplied betas, user_profile_id, and extra_headers must all # survive parse()'s hand-written merge alongside the injected # structured-outputs beta and the helper tag respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=_message_json())) client.beta.messages.parse( model="claude-sonnet-4-5", max_tokens=16, messages=[{"role": "user", "content": "hello"}], betas=["fake-beta-2026-01-01"], user_profile_id="upi_123", extra_headers={"X-Custom": "1", "X-Stainless-Helper": "caller-tag"}, ) headers = respx_mock.calls.last.request.headers # injected structured-outputs beta is appended to caller betas, not dropped assert headers["anthropic-beta"] == "fake-beta-2026-01-01,structured-outputs-2025-12-15" assert headers["anthropic-user-profile-id"] == "upi_123" assert headers["X-Custom"] == "1" # helper tag accumulates with the caller's on one line (append-header semantics) assert headers.get_list(STAINLESS_HELPER_HEADER) == ["beta.messages.parse, caller-tag"] @pytest.mark.skipif(_compat.PYDANTIC_V1, reason="parse() response post-parser is pydantic-v2 only") def test_parse_caller_beta_header_overrides(self, client: Anthropic, respx_mock: respx.MockRouter) -> None: # extra_headers win outright on non-append headers, matching create() respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=_message_json())) client.beta.messages.parse( model="claude-sonnet-4-5", max_tokens=16, messages=[{"role": "user", "content": "hello"}], extra_headers={"anthropic-beta": "explicit-only"}, ) headers = respx_mock.calls.last.request.headers assert headers["anthropic-beta"] == "explicit-only" @pytest.mark.respx(base_url=base_url) class TestAsyncWireHeaders: async def test_caller_tag_is_appended_not_clobbered( self, async_client: AsyncAnthropic, respx_mock: respx.MockRouter ) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=_message_json())) tool = cast("BetaToolParam", _TaggedDict({"name": "t", "description": "d", "input_schema": {"type": "object"}})) tag_helper(tool, "mcp_tool") await async_client.beta.messages.create( model="claude-sonnet-4-5", max_tokens=16, messages=[{"role": "user", "content": "hello"}], tools=[tool], extra_headers={"X-Stainless-Helper": "caller-tag"}, ) request = respx_mock.calls.last.request values = request.headers.get_list(STAINLESS_HELPER_HEADER) assert values == ["mcp_tool, caller-tag"] @pytest.mark.skipif(_compat.PYDANTIC_V1, reason="parse() response post-parser is pydantic-v2 only") async def test_parse_sends_single_header_line( self, async_client: AsyncAnthropic, respx_mock: respx.MockRouter ) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=_message_json())) await async_client.beta.messages.parse( model="claude-sonnet-4-5", max_tokens=16, messages=[{"role": "user", "content": "hello"}], ) request = respx_mock.calls.last.request values = request.headers.get_list(STAINLESS_HELPER_HEADER) assert values == ["beta.messages.parse"] @pytest.mark.skipif(_compat.PYDANTIC_V1, reason="parse() response post-parser is pydantic-v2 only") async def test_parse_merges_caller_extra_headers( self, async_client: AsyncAnthropic, respx_mock: respx.MockRouter ) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=_message_json())) await async_client.beta.messages.parse( model="claude-sonnet-4-5", max_tokens=16, messages=[{"role": "user", "content": "hello"}], betas=["fake-beta-2026-01-01"], user_profile_id="upi_123", extra_headers={"X-Custom": "1", "X-Stainless-Helper": "caller-tag"}, ) headers = respx_mock.calls.last.request.headers assert headers["anthropic-beta"] == "fake-beta-2026-01-01,structured-outputs-2025-12-15" assert headers["anthropic-user-profile-id"] == "upi_123" assert headers["X-Custom"] == "1" assert headers.get_list(STAINLESS_HELPER_HEADER) == ["beta.messages.parse, caller-tag"] @pytest.mark.skipif(_compat.PYDANTIC_V1, reason="parse() response post-parser is pydantic-v2 only") async def test_parse_caller_beta_header_overrides( self, async_client: AsyncAnthropic, respx_mock: respx.MockRouter ) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=_message_json())) await async_client.beta.messages.parse( model="claude-sonnet-4-5", max_tokens=16, messages=[{"role": "user", "content": "hello"}], extra_headers={"anthropic-beta": "explicit-only"}, ) headers = respx_mock.calls.last.request.headers assert headers["anthropic-beta"] == "explicit-only" anthropic-sdk-python-0.120.2/tests/lib/test_vertex.py000066400000000000000000000335701523216435200226120ustar00rootroot00000000000000from __future__ import annotations import os import sys from typing import Any, cast from unittest.mock import Mock from typing_extensions import Protocol import httpx import pytest from respx import MockRouter from anthropic import AnthropicVertex, AsyncAnthropicVertex from anthropic.lib.vertex._auth import refresh_auth from anthropic.lib._extras._common import MissingDependencyError base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") class MockRequestCall(Protocol): request: httpx.Request class TestAnthropicVertex: client = AnthropicVertex(region="region", project_id="project", access_token="my-access-token") @pytest.mark.respx() def test_messages_retries(self, respx_mock: MockRouter) -> None: request_url = "https://region-aiplatform.googleapis.com/v1/projects/project/locations/region/publishers/anthropic/models/claude-3-sonnet@20240229:rawPredict" respx_mock.post(request_url).mock( side_effect=[ httpx.Response(500, json={"error": "server error"}, headers={"retry-after-ms": "10"}), httpx.Response(200, json={"foo": "bar"}), ] ) self.client.messages.create( max_tokens=1024, messages=[ { "role": "user", "content": "Say hello there!", } ], model="claude-3-sonnet@20240229", ) calls = cast("list[MockRequestCall]", respx_mock.calls) assert len(calls) == 2 assert calls[0].request.url == request_url assert calls[1].request.url == request_url def test_copy(self) -> None: copied = self.client.copy() assert id(copied) != id(self.client) copied = self.client.copy(region="another-region", project_id="another-project") assert copied.region == "another-region" assert self.client.region == "region" assert copied.project_id == "another-project" assert self.client.project_id == "project" def test_with_options(self) -> None: copied = self.client.with_options(region="another-region", project_id="another-project") assert copied.region == "another-region" assert self.client.region == "region" assert copied.project_id == "another-project" assert self.client.project_id == "project" def test_copy_default_options(self) -> None: # options that have a default are overridden correctly copied = self.client.copy(max_retries=7) assert copied.max_retries == 7 assert self.client.max_retries == 2 copied2 = copied.copy(max_retries=6) assert copied2.max_retries == 6 assert copied.max_retries == 7 # timeout assert isinstance(self.client.timeout, httpx.Timeout) copied = self.client.copy(timeout=None) assert copied.timeout is None assert isinstance(self.client.timeout, httpx.Timeout) def test_copy_default_headers(self) -> None: client = AnthropicVertex( base_url=base_url, region="region", project_id="project", _strict_response_validation=True, default_headers={"X-Foo": "bar"}, ) assert client.default_headers["X-Foo"] == "bar" # does not override the already given value when not specified copied = client.copy() assert copied.default_headers["X-Foo"] == "bar" # merges already given headers copied = client.copy(default_headers={"X-Bar": "stainless"}) assert copied.default_headers["X-Foo"] == "bar" assert copied.default_headers["X-Bar"] == "stainless" # uses new values for any already given headers copied = client.copy(default_headers={"X-Foo": "stainless"}) assert copied.default_headers["X-Foo"] == "stainless" # set_default_headers # completely overrides already set values copied = client.copy(set_default_headers={}) assert copied.default_headers.get("X-Foo") is None copied = client.copy(set_default_headers={"X-Bar": "Robert"}) assert copied.default_headers["X-Bar"] == "Robert" with pytest.raises( ValueError, match="`default_headers` and `set_default_headers` arguments are mutually exclusive", ): client.copy(set_default_headers={}, default_headers={"X-Foo": "Bar"}) def test_copy_x_stainless_helper_header_appends(self) -> None: # `x-stainless-helper` accumulates across copies instead of being clobbered client = AnthropicVertex( base_url=base_url, region="region", project_id="project", _strict_response_validation=True, default_headers={"x-stainless-helper": "parent"}, ) copied = client.copy(default_headers={"x-stainless-helper": "child"}) assert copied.default_headers["x-stainless-helper"] == "parent, child" def test_global_region_base_url(self) -> None: """Test that global region uses the correct base URL.""" client = AnthropicVertex(region="global", project_id="test-project", access_token="fake-token") assert str(client.base_url).rstrip("/") == "https://aiplatform.googleapis.com/v1" def test_us_region_base_url(self) -> None: """Test that us region uses the correct base URL.""" client = AnthropicVertex(region="us", project_id="test-project", access_token="fake-token") assert str(client.base_url).rstrip("/") == "https://aiplatform.us.rep.googleapis.com/v1" def test_eu_region_base_url(self) -> None: """Test that us region uses the correct base URL.""" client = AnthropicVertex(region="eu", project_id="test-project", access_token="fake-token") assert str(client.base_url).rstrip("/") == "https://aiplatform.eu.rep.googleapis.com/v1" @pytest.mark.parametrize("region", ["us-central1", "europe-west1", "asia-southeast1"]) def test_regional_base_url(self, region: str) -> None: """Test that regional endpoints use the correct base URL format.""" client = AnthropicVertex(region=region, project_id="test-project", access_token="fake-token") expected_url = f"https://{region}-aiplatform.googleapis.com/v1" assert str(client.base_url).rstrip("/") == expected_url def test_env_var_base_url_override(self, monkeypatch: pytest.MonkeyPatch) -> None: """Test that ANTHROPIC_VERTEX_BASE_URL environment variable does not override client arg.""" test_url = "https://custom-endpoint.googleapis.com/v1" monkeypatch.setenv("ANTHROPIC_VERTEX_BASE_URL", test_url) client = AnthropicVertex( region="global", # we expect this to get ignored since the user is providing a base_url project_id="test-project", access_token="fake-token", base_url="https://test.googleapis.com/v1", ) assert str(client.base_url).rstrip("/") == "https://test.googleapis.com/v1" def test_refresh_without_google_auth_raises_actionable_error(monkeypatch: pytest.MonkeyPatch) -> None: # `None` in sys.modules makes the import fail even when google-auth is installed. monkeypatch.setitem(sys.modules, "google.auth.transport.requests", cast(Any, None)) with pytest.raises(MissingDependencyError, match=r"anthropic\[vertex\]"): refresh_auth(cast(Any, Mock())) class TestAsyncAnthropicVertex: client = AsyncAnthropicVertex(region="region", project_id="project", access_token="my-access-token") @pytest.mark.respx() @pytest.mark.asyncio() async def test_messages_retries(self, respx_mock: MockRouter) -> None: request_url = "https://region-aiplatform.googleapis.com/v1/projects/project/locations/region/publishers/anthropic/models/claude-3-sonnet@20240229:rawPredict" respx_mock.post(request_url).mock( side_effect=[ httpx.Response(500, json={"error": "server error"}, headers={"retry-after-ms": "10"}), httpx.Response(200, json={"foo": "bar"}), ] ) await self.client.with_options(timeout=0.2).messages.create( max_tokens=1024, messages=[ { "role": "user", "content": "Say hello there!", } ], model="claude-3-sonnet@20240229", ) calls = cast("list[MockRequestCall]", respx_mock.calls) assert len(calls) == 2 assert calls[0].request.url == request_url assert calls[1].request.url == request_url def test_copy(self) -> None: copied = self.client.copy() assert id(copied) != id(self.client) copied = self.client.copy(region="another-region", project_id="another-project") assert copied.region == "another-region" assert self.client.region == "region" assert copied.project_id == "another-project" assert self.client.project_id == "project" def test_with_options(self) -> None: copied = self.client.with_options(region="another-region", project_id="another-project") assert copied.region == "another-region" assert self.client.region == "region" assert copied.project_id == "another-project" assert self.client.project_id == "project" def test_copy_default_options(self) -> None: # options that have a default are overridden correctly copied = self.client.copy(max_retries=7) assert copied.max_retries == 7 assert self.client.max_retries == 2 copied2 = copied.copy(max_retries=6) assert copied2.max_retries == 6 assert copied.max_retries == 7 # timeout assert isinstance(self.client.timeout, httpx.Timeout) copied = self.client.copy(timeout=None) assert copied.timeout is None assert isinstance(self.client.timeout, httpx.Timeout) def test_copy_default_headers(self) -> None: client = AsyncAnthropicVertex( base_url=base_url, region="region", project_id="project", _strict_response_validation=True, default_headers={"X-Foo": "bar"}, ) assert client.default_headers["X-Foo"] == "bar" # does not override the already given value when not specified copied = client.copy() assert copied.default_headers["X-Foo"] == "bar" # merges already given headers copied = client.copy(default_headers={"X-Bar": "stainless"}) assert copied.default_headers["X-Foo"] == "bar" assert copied.default_headers["X-Bar"] == "stainless" # uses new values for any already given headers copied = client.copy(default_headers={"X-Foo": "stainless"}) assert copied.default_headers["X-Foo"] == "stainless" # set_default_headers # completely overrides already set values copied = client.copy(set_default_headers={}) assert copied.default_headers.get("X-Foo") is None copied = client.copy(set_default_headers={"X-Bar": "Robert"}) assert copied.default_headers["X-Bar"] == "Robert" with pytest.raises( ValueError, match="`default_headers` and `set_default_headers` arguments are mutually exclusive", ): client.copy(set_default_headers={}, default_headers={"X-Foo": "Bar"}) def test_copy_x_stainless_helper_header_appends(self) -> None: # `x-stainless-helper` accumulates across copies instead of being clobbered client = AsyncAnthropicVertex( base_url=base_url, region="region", project_id="project", _strict_response_validation=True, default_headers={"x-stainless-helper": "parent"}, ) copied = client.copy(default_headers={"x-stainless-helper": "child"}) assert copied.default_headers["x-stainless-helper"] == "parent, child" def test_global_region_base_url(self) -> None: """Test that global region uses the correct base URL.""" client = AsyncAnthropicVertex(region="global", project_id="test-project", access_token="fake-token") assert str(client.base_url).rstrip("/") == "https://aiplatform.googleapis.com/v1" def test_us_region_base_url(self) -> None: """Test that us region uses the correct base URL.""" client = AsyncAnthropicVertex(region="us", project_id="test-project", access_token="fake-token") assert str(client.base_url).rstrip("/") == "https://aiplatform.us.rep.googleapis.com/v1" def test_eu_region_base_url(self) -> None: """Test that eu region uses the correct base URL.""" client = AsyncAnthropicVertex(region="eu", project_id="test-project", access_token="fake-token") assert str(client.base_url).rstrip("/") == "https://aiplatform.eu.rep.googleapis.com/v1" def test_regional_base_url(self) -> None: """Test that regional endpoints use the correct base URL format.""" test_regions = ["us-central1", "europe-west1", "asia-southeast1"] for region in test_regions: client = AsyncAnthropicVertex(region=region, project_id="test-project", access_token="fake-token") expected_url = f"https://{region}-aiplatform.googleapis.com/v1" assert str(client.base_url).rstrip("/") == expected_url def test_env_var_base_url_override(self, monkeypatch: pytest.MonkeyPatch) -> None: """Test that ANTHROPIC_VERTEX_BASE_URL environment variable does not override client arg.""" test_url = "https://custom-endpoint.googleapis.com/v1" monkeypatch.setenv("ANTHROPIC_VERTEX_BASE_URL", test_url) client = AsyncAnthropicVertex( region="global", # we expect this to get ignored since the user is providing a base_url project_id="test-project", access_token="fake-token", base_url="https://test.googleapis.com/v1", ) assert str(client.base_url).rstrip("/") == "https://test.googleapis.com/v1" anthropic-sdk-python-0.120.2/tests/lib/tools/000077500000000000000000000000001523216435200210145ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/tools/__init__.py000066400000000000000000000000001523216435200231130ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/tools/__inline_snapshot__/000077500000000000000000000000001523216435200250055ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/tools/__inline_snapshot__/test_runners/000077500000000000000000000000001523216435200275405ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools/000077500000000000000000000000001523216435200330225ustar00rootroot00000000000000092be1de-d3f8-4c22-a4ea-a7ad54689836.json000066400000000000000000000264571523216435200406040ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools[ { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages?beta=true", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "600", "accept": "application/json", "content-type": "application/json", "user-agent": "Anthropic/Python 0.84.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.84.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.12.6", "x-stainless-async": "false", "anthropic-version": "2023-06-01", "x-stainless-helper": "BetaToolRunner", "anthropic-beta": "structured-outputs-2025-12-15", "x-stainless-retry-count": "0", "x-stainless-read-timeout": "600", "content-length": "660" }, "body": { "max_tokens": 1024, "messages": [ { "role": "user", "content": "What is the weather in SF, NY, and London in Celsius?" } ], "model": "claude-opus-4-5", "tools": [ { "name": "get_weather", "description": "Lookup the weather for a given city in either celsius or fahrenheit", "input_schema": { "additionalProperties": false, "properties": { "location": { "description": "The city and state, e.g. San Francisco, CA", "title": "Location", "type": "string" }, "units": { "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit", "enum": [ "c", "f" ], "title": "Units", "type": "string" } }, "required": [ "location", "units" ], "type": "object" }, "allowed_callers": [ "code_execution_20260120" ] } ] } }, "response": { "status_code": 200, "headers": { "content-type": "application/json", "connection": "keep-alive", "anthropic-ratelimit-input-tokens-limit": "30000", "anthropic-ratelimit-input-tokens-remaining": "28000", "anthropic-ratelimit-input-tokens-reset": "2026-02-25T20:02:37Z", "anthropic-ratelimit-output-tokens-limit": "8000", "anthropic-ratelimit-output-tokens-remaining": "8000", "anthropic-ratelimit-output-tokens-reset": "2026-02-25T20:02:36Z", "anthropic-ratelimit-requests-limit": "50", "anthropic-ratelimit-requests-remaining": "49", "anthropic-ratelimit-requests-reset": "2026-02-25T20:02:32Z", "anthropic-ratelimit-tokens-limit": "38000", "anthropic-ratelimit-tokens-remaining": "36000", "anthropic-ratelimit-tokens-reset": "2026-02-25T20:02:36Z", "x-robots-tag": "none", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "content-security-policy": "default-src 'none'; frame-ancestors 'none'", "cf-cache-status": "DYNAMIC" }, "body": { "model": "claude-opus-4-5-20251101", "id": "msg_01LAwUK3MeXUxoNW5ZgnPpGy", "type": "message", "role": "assistant", "content": [ { "type": "text", "text": "I'll check the weather for all three cities in Celsius simultaneously." }, { "type": "server_tool_use", "id": "srvtoolu_017SzB73BSitzNFo1Xb3bq7S", "name": "code_execution", "input": { "code": "import json\n\n# Get weather for all three cities in Celsius\nsf_weather = await get_weather({\"location\": \"San Francisco, CA\", \"units\": \"c\"})\nny_weather = await get_weather({\"location\": \"New York, NY\", \"units\": \"c\"})\nlondon_weather = await get_weather({\"location\": \"London, UK\", \"units\": \"c\"})\n\n# Parse and display results\nsf = json.loads(sf_weather)\nny = json.loads(ny_weather)\nlondon = json.loads(london_weather)\n\nprint(\"Weather in Celsius:\")\nprint(f\"San Francisco: {sf}\")\nprint(f\"New York: {ny}\")\nprint(f\"London: {london}\")\n" }, "caller": { "type": "direct" } }, { "type": "tool_use", "id": "toolu_011MDRpaZRMRRjtFkJizD6nS", "name": "get_weather", "input": { "location": "San Francisco, CA", "units": "c" }, "caller": { "type": "code_execution_20260120", "tool_id": "srvtoolu_017SzB73BSitzNFo1Xb3bq7S" } } ], "container": { "id": "container_011CYVPF4iP8oD6Vsz1NhVih", "expires_at": "2026-02-25T20:07:37.475893Z" }, "stop_reason": "tool_use", "stop_sequence": null, "usage": { "input_tokens": 3182, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": { "ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0 }, "output_tokens": 237, "service_tier": "standard", "inference_geo": "not_available", "server_tool_use": { "web_search_requests": 0, "web_fetch_requests": 0 } } } } }, { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages?beta=true", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "600", "accept": "application/json", "content-type": "application/json", "user-agent": "Anthropic/Python 0.84.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.84.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.12.6", "x-stainless-async": "false", "anthropic-version": "2023-06-01", "x-stainless-helper": "BetaToolRunner", "anthropic-beta": "structured-outputs-2025-12-15", "x-stainless-retry-count": "0", "x-stainless-read-timeout": "600", "content-length": "1978" }, "body": { "max_tokens": 1024, "messages": [ { "role": "user", "content": "What is the weather in SF, NY, and London in Celsius?" }, { "role": "assistant", "content": [ { "text": "I'll check the weather for all three cities in Celsius simultaneously.", "type": "text" }, { "id": "srvtoolu_017SzB73BSitzNFo1Xb3bq7S", "input": { "code": "import json\n\n# Get weather for all three cities in Celsius\nsf_weather = await get_weather({\"location\": \"San Francisco, CA\", \"units\": \"c\"})\nny_weather = await get_weather({\"location\": \"New York, NY\", \"units\": \"c\"})\nlondon_weather = await get_weather({\"location\": \"London, UK\", \"units\": \"c\"})\n\n# Parse and display results\nsf = json.loads(sf_weather)\nny = json.loads(ny_weather)\nlondon = json.loads(london_weather)\n\nprint(\"Weather in Celsius:\")\nprint(f\"San Francisco: {sf}\")\nprint(f\"New York: {ny}\")\nprint(f\"London: {london}\")\n" }, "name": "code_execution", "type": "server_tool_use", "caller": { "type": "direct" } }, { "id": "toolu_011MDRpaZRMRRjtFkJizD6nS", "input": { "location": "San Francisco, CA", "units": "c" }, "name": "get_weather", "type": "tool_use", "caller": { "tool_id": "srvtoolu_017SzB73BSitzNFo1Xb3bq7S", "type": "code_execution_20260120" } } ] }, { "role": "user", "content": [ { "type": "tool_result", "tool_use_id": "toolu_011MDRpaZRMRRjtFkJizD6nS", "content": "{\"location\": \"San Francisco, CA\", \"temperature\": \"20\\u00b0C\", \"condition\": \"Sunny\"}" } ] } ], "model": "claude-opus-4-5", "container": "container_011CYVPF4iP8oD6Vsz1NhVih", "tools": [ { "name": "get_weather", "description": "Lookup the weather for a given city in either celsius or fahrenheit", "input_schema": { "additionalProperties": false, "properties": { "location": { "description": "The city and state, e.g. San Francisco, CA", "title": "Location", "type": "string" }, "units": { "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit", "enum": [ "c", "f" ], "title": "Units", "type": "string" } }, "required": [ "location", "units" ], "type": "object" }, "allowed_callers": [ "code_execution_20260120" ] } ] } }, "response": { "status_code": 200, "headers": { "content-type": "application/json", "connection": "keep-alive", "x-robots-tag": "none", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "content-security-policy": "default-src 'none'; frame-ancestors 'none'", "cf-cache-status": "DYNAMIC" }, "body": { "model": "claude-opus-4-5-20251101", "id": "msg_0147NV7w8PyZ6bSUsNY79cYj", "type": "message", "role": "assistant", "content": [ { "type": "tool_use", "id": "toolu_01RXQDRjwv5Un7n98xFahjo8", "name": "get_weather", "input": { "location": "New York, NY", "units": "c" }, "caller": { "type": "code_execution_20260120", "tool_id": "srvtoolu_017SzB73BSitzNFo1Xb3bq7S" } } ], "container": { "id": "container_011CYVPF4iP8oD6Vsz1NhVih", "expires_at": "2026-02-25T20:07:39.078916Z" }, "stop_reason": "tool_use", "stop_sequence": null, "usage": { "input_tokens": 0, "output_tokens": 0, "server_tool_use": { "web_search_requests": 0, "web_fetch_requests": 0 } } } } } ]10e53c1d-51be-4c64-b5bf-99adb3fa4719.json000066400000000000000000000175111523216435200406410ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools[ { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages?beta=true", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "600", "accept": "application/json", "content-type": "application/json", "user-agent": "Anthropic/Python 0.82.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.82.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.9.18", "x-stainless-async": "false", "anthropic-version": "2023-06-01", "x-stainless-helper": "BetaToolRunner", "anthropic-beta": "structured-outputs-2025-12-15", "x-stainless-retry-count": "0", "x-stainless-read-timeout": "600", "content-length": "588" }, "body": { "max_tokens": 1024, "messages": [ { "role": "user", "content": "What is the weather in SF?" } ], "model": "claude-haiku-4-5", "tools": [ { "name": "get_weather", "description": "Lookup the weather for a given city in either celsius or fahrenheit", "input_schema": { "additionalProperties": false, "properties": { "location": { "description": "The city and state, e.g. San Francisco, CA", "title": "Location", "type": "string" }, "units": { "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit", "enum": [ "c", "f" ], "title": "Units", "type": "string" } }, "required": [ "location", "units" ], "type": "object" } } ] } }, "response": { "status_code": 200, "headers": { "content-type": "application/json", "connection": "keep-alive", "x-robots-tag": "none", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "content-security-policy": "default-src 'none'; frame-ancestors 'none'", "cf-cache-status": "DYNAMIC" }, "body": { "model": "claude-haiku-4-5-20251001", "id": "msg_01RMQBcKf2dxTq6qfi31BBTz", "type": "message", "role": "assistant", "content": [ { "type": "tool_use", "id": "toolu_01A9HHF5Ezy3oBrKmSgfASm9", "name": "get_weather", "input": { "location": "San Francisco, CA", "units": "f" }, "caller": { "type": "direct" } } ], "stop_reason": "tool_use", "stop_sequence": null, "usage": { "input_tokens": 656, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": { "ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0 }, "output_tokens": 74, "service_tier": "standard", "inference_geo": "not_available" } } } }, { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages?beta=true", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "600", "accept": "application/json", "content-type": "application/json", "user-agent": "Anthropic/Python 0.82.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.82.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.9.18", "x-stainless-async": "false", "anthropic-version": "2023-06-01", "x-stainless-helper": "BetaToolRunner", "anthropic-beta": "structured-outputs-2025-12-15", "x-stainless-retry-count": "0", "x-stainless-read-timeout": "600", "content-length": "950" }, "body": { "max_tokens": 1024, "messages": [ { "role": "user", "content": "What is the weather in SF?" }, { "role": "assistant", "content": [ { "id": "toolu_01A9HHF5Ezy3oBrKmSgfASm9", "input": { "location": "San Francisco, CA", "units": "f" }, "name": "get_weather", "type": "tool_use", "caller": { "type": "direct" } } ] }, { "role": "user", "content": [ { "type": "tool_result", "tool_use_id": "toolu_01A9HHF5Ezy3oBrKmSgfASm9", "content": "RuntimeError('Unexpected error, try again')", "is_error": true } ] } ], "model": "claude-haiku-4-5", "tools": [ { "name": "get_weather", "description": "Lookup the weather for a given city in either celsius or fahrenheit", "input_schema": { "additionalProperties": false, "properties": { "location": { "description": "The city and state, e.g. San Francisco, CA", "title": "Location", "type": "string" }, "units": { "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit", "enum": [ "c", "f" ], "title": "Units", "type": "string" } }, "required": [ "location", "units" ], "type": "object" } } ] } }, "response": { "status_code": 200, "headers": { "content-type": "application/json", "connection": "keep-alive", "x-robots-tag": "none", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "content-security-policy": "default-src 'none'; frame-ancestors 'none'", "cf-cache-status": "DYNAMIC" }, "body": { "model": "claude-haiku-4-5-20251001", "id": "msg_01GJyhkguJrrqMbZNzEybYFL", "type": "message", "role": "assistant", "content": [ { "type": "text", "text": "I apologize, but I'm getting an error when trying to fetch the weather for San Francisco. This appears to be a temporary issue with the weather service. Could you try again in a moment, or let me know if you'd like me to attempt to retrieve the weather for a different location?" } ], "stop_reason": "end_turn", "stop_sequence": null, "usage": { "input_tokens": 760, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": { "ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0 }, "output_tokens": 63, "service_tier": "standard", "inference_geo": "not_available" } } } } ]32da0815-2270-4d29-87be-3b5b63ab42e2.json000066400000000000000000000723241523216435200403320ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools[ { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages?beta=true", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "600", "accept": "application/json", "content-type": "application/json", "user-agent": "Anthropic/Python 0.82.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.82.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.9.18", "x-stainless-async": "false", "anthropic-version": "2023-06-01", "x-stainless-helper": "BetaToolRunner", "anthropic-beta": "structured-outputs-2025-12-15", "x-stainless-retry-count": "0", "x-stainless-read-timeout": "600", "content-length": "175" }, "body": { "max_tokens": 1024, "messages": [ { "role": "user", "content": "What is the weather in SF?" } ], "model": "claude-haiku-4-5", "tools": [ { "type": "web_search_20250305", "name": "web_search" } ] } }, "response": { "status_code": 200, "headers": { "content-type": "application/json", "connection": "keep-alive", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "cf-cache-status": "DYNAMIC", "x-robots-tag": "none", "content-security-policy": "default-src 'none'; frame-ancestors 'none'" }, "body": { "model": "claude-haiku-4-5-20251001", "id": "msg_011Y4sbfjCq85yJrUA13Dxb3", "type": "message", "role": "assistant", "content": [ { "type": "server_tool_use", "id": "srvtoolu_01JCzA8PXrZDNPK9pSt8KCE9", "name": "web_search", "input": { "query": "San Francisco weather today" }, "caller": { "type": "direct" } }, { "type": "web_search_tool_result", "tool_use_id": "srvtoolu_01JCzA8PXrZDNPK9pSt8KCE9", "content": [ { "type": "web_search_result", "title": "San Francisco, CA Weather Forecast | AccuWeather", "url": "https://www.accuweather.com/en/us/san-francisco/94103/weather-forecast/347629", "encrypted_content": "EsUUCioIDBgCIiQ1NTc2NjExZi1hMWVlLTQyN2MtOTgwMC0yOThkOTU3OTg5OWMSDLIgSIOZVJN7rBzL2RoMeuDVIdKMFXTOEcD/IjDzAb5B+tZs0xD2qRkGITF3bpNuI7vBI426IwLIrF7VjbHMxGSUkrSD1FAre74YxhoqyBPw+ilkFlQq9WprRvnPBoeHgriF1/OasvF6DSeJNUrvtRKs2e4QrfFnhXtiN95pGBJjyy9TW7k7oeqWN0dA6AgjRP0BOW/IpspIJyVhzGl/If1Yg9l+KN0K+El+tPe0z1CdzFcONPB4SlVKTCDIVmAEVfl0d/mAjF4xHKWFo8WBU297Ut5qacIfeCH9VxUXWQ51IRxmsSP4+n1qOOOPz3vnD6f7lJRJLAXUj2+9sQncylmUs5zzlLIZg/VP+SaBvLIm+RjH9dvVHQd7Zsm7F0rQhCHh3EiSzxG42R6ioVAqAEiVcDgsV1x73p78FJi4ewCIuoaR9b5Raym00XHGFQghc807ibv6MKqvrYoSXIGIDyj+2m5nxNZs8kiecFSLYpMYNGjDb213sU2K7qppborOdgFi3U+jA462Hv31KnT6rQ1cTtEIzys04TgMANIk6l0LJ+J/BfxtzwTG9u8DXHrkfQcI4mz0g4wQQOZPhozAYA3WAom4xlLngI7bcasaL3iUottur/Hi0Rwwz8afCwrXshqkpLUxvnhqmjOX6F8mqq50oPv4FCmHAlkoEdU3h5Htb8ONsxLWtc32XhxoXXTlQ/bRO197gwtcknDeg7kR1e7Bv316e4t2mupuDhmK1y3syEkbB8OyvVJE8Qmg3xxikEdxbGbNk6WTpz/Gsb2wU3DU4iirGaWT7H1fz/tyGM6FjVfB3O+nwdaRZ6tAbuVhG9Mwj50CmPVj/qImqLsu/CugVks9hqrCD7ZZm9fpJY1pWvrxDTcxjPRbx++/DPor7Kiu1iYiBXhFR90pqQi0lcGeNwZj7JEZItL5qvQqUhdoBoNwjb92DLPs0wUHQxH6VEolkEbY1sWrzTESBo9dbwhJbImvCwoBNRiXXUe8JV1WTZ83SIcUNq/tdDr/ALNSKSVC9Qn3XvfrMvGD4icA2ICn6Bx2QZtFdfGXLl912Z5THaiuIEhcBeeHJ/Cjm9spfziI33tfpg8VVCVwixZrmlH2T2/tFuRvrb+6fyTR6yu23dr4NFPCuyIx+j5rqy45+n86984+M9xpxRdHVft1rSvU2t+4v8Swmt/3tH5VD/TjeKoExI68OoKfXc12K5yASHo701SpSBCkxW+ATRqeDT13/qUOkV5RVfsnDDJP5ZyJaHG31QrqLRLLLYXfCnUmrIFHsQN05VuHxtUZLSvupGzAN50zGNMIjNRBC2z1p9tWfzwdsKjh3JuzS29MHsflyx2AmTUDuFZmQUWVNWb2Uo9CcXO3/l/4/skBxK7McCAv+7EH9IImesDm5VkUq2D/9bQJEBPpJlLgGP/EbIAaR9AvATIQq6dxBbD9KzZgw4bUNLN7m6o+7xr6hoFscMAQxaj9F5+D1dmsJYncBL+eAEiUH5lgz3iunUbR9FbCBmUxhdnIcjxF5Bdmbisy8xHAFtyOJ4lAJd0PS7G2MPtNXZIWonoJ9kxTcxK05VGaLd1HKm6kNNaCLr4c4v0yKYxFLizimZQKreI0dMj2GfAqXy2NvCP1YVYCRGyumx8qRm8w43Ub4JDPUUbKywToGlh6089USTLDNdwZL5gi1NkezCJye0PZlgvSCouLBT5/0IKA3yM4CFCR9vPSn9LrGORKBgSsvpeTzgBmcpzt+plmKGCarmhSzFgD5jyR+xrmvIyPkK333Sk3xf3RADmLyjBIk171FmJ3pQ59h1jPZYLssEUWSLwvEcNbSnI5L+l6ss4VyPriZz14iln3zxqpiRiwmI+GNJFZu702toIyNdyZ0I9e1mmD5c91vhfI5lHmvLAK6YwM+p82j2/9TvC7gLsL+dpQl0kNdUsaUdvu+mjvyqjekUne9zVJrINuXr4+0qFq59OE1WHV5dKc59Tg9V2pfBlMI2hwtDhB9w7m1Nh8dTt5pTB0ZHbeQSg4kn1sZQ/aQCwvAyCa+LTdAHFXMvO53w/wctqvDRtSZVcIY6jodS82DZS20HP2Sk76qaAok82bdLs+gUeAiluxvOANwxBDhje7VTHc1vxtV1dAXwhHsMa9BSueuibS53SUVfAih5CP7WqiJo1OTYvKXdKA8SToMpCfxCxc5akKuIf5tYmRV7Kx7aK4erOskMyru2o6z3jtSyB1JgFKbBeP2jA0yrPQ3nNLsDCVX4BHHM6QZRzDrh7hualZkvqXbg6UWcrY0FeXeVToCpmZ2dULhvUj9+zBAK18GRdDSl5uJR2xCLPC2qDWayOVxFOnia5NzK01CHn0RIjUn5nxszWluk9R+Vn3D9WP9YKVijkEFglPVd/rP4gX8NYXmpunN/j3Ksl7mY2N34wjb17hHa6TFcDNlDMI2ihXMpD4GsYZPpcPqzeF8VhHGO6c2hfm2VJRnasnmByP+aq9Iq08mEA1fpRxzcysgfCca94E4+ikRj6q5fFFsH7SM/QlZa6wNA5aoNh4MQxIcuIolaerYItM7esLa7ft9GeIDIFpZP36yRyD6nRulrQmRj9RmlRF9ti1C37kqROPJGZt8EzX9rh1bLxfi7fHHjjoobIOhbDNi7fTBedn1SL+wLElX3W9l0pXK87XPoT+DKE6xMaabkpmmrm8DfdUf/cGoKXvyLSSojU+Axz7/+EDq0bn6TJW6yqfEHMSK03jsGuQCvdkkbLtCJzU0G0DSh8o4nolkXP+7rLL9wREpbN+81LsuaWCWbClGIyAxL+wKixfLTdvbtyuy7dB3WFX0z7zfVUBvkUV74ZV2Ta7WwCytQ3SEt43bgLlqfUi7dRG+twma6B4FAXrNHgT8s1qwrUFi2dcSBVzpZLcBdyWXxha4XW7g40J/QOTY9ujA2olAdM4OkK2zF8f4xAej0+wB3QX2IqQSgxp9sEC3IVIT+GL8Gew2d3D71trEoomM7DnqkVw1TiRn/4jYMqobkbbsVQ+K3XmZqcOD4sJ8rpJCRT9XekXbZDZY79xtsEFw74EaPqVNWtfkw3nvth2iWo1A1T+2HZ2t3Jjfe6WMWgRpzSDicTBmNmT5rDz3lA7zfJWoD7VQCXY0uUbWZd03TyKGI5DvjyXY/1whsqIMf2lKQXNn6NZk4mmmSwjd7A8k006KjA/HPuj23YOeA4Nwd+ZbkJit81NwUex6FN7QV6Go+eSIFuitK37EYiK6ZN0Cek/RllZNeMf6ilQp22BlGHVymwgfQOWrzaE/xFU6fJi7kwfU7fjVguqS07XV3hNNjWsqpJ7zkG7XoWcDoWkKE4otq495Asq8yv/JnrpDVvJCejdx2lYf+UrAvr4jqa0nw5ksv74LOv/3aCtzlXov2nBvx6efJRvW09MpQprBcV+CfrAa3mr+5D68WCHrrDaK3qZAqs/bRir0hgD", "page_age": "6 hours ago" }, { "type": "web_search_result", "title": "San Francisco, CA Hourly Weather Forecast | Weather Underground", "url": "https://www.wunderground.com/hourly/us/ca/san-francisco", "encrypted_content": "EtIICioIDBgCIiQ1NTc2NjExZi1hMWVlLTQyN2MtOTgwMC0yOThkOTU3OTg5OWMSDP2jlVXFw/S3qV+nehoMaXKVBk/Og11Mf2TIIjAQwGacQ3ApNS+XGb5d75pLGC48urqy2mQEEFGfyF50dnhrTTGZ8rj99QiXNcnTq4Qq1QdCUL3z664of54MGgv/+9fGfLysTo0Xpx92lFJgO7xqfifTPv7eYuMoUfAlpFrA7PfCmdeNWUuSQeTy65g0pJehlZejByZurdbfi2S8QPGlRv6zWYV5Ijh9HlMPMN3a1zWnmv1Klc7cuzWIRw4BImWDGjGOs1AJ2/ggEKXnqlMHrk3eqMVR5Bgpo8eSEscArvg7NVJs7S42qCUGo5Y2HsB8+846wmui1pDhrseSFYCJBK4LtgW/hzy64zstg553JFW7o3x4Vy3Ezxqip8TW2TkOPNeax0bzpEDfMpiwIy0Iqtn2Ai/qYbdcqCGz0X+wVG4rwv/JyVvUislZATIs/EE0GJqm01nM2uM2Cf00CC5U2OdzjcKkbPdJ8vf4p+QdQB2TNK+oS2QKVG1WTVgHgJQpukOzugedUyiVs/bLSo3PqqLPQQTEO/YaNR6fjifAzMLfagrlM/gAKQ7bNDp4M74hnFOWk13tZqbKy4p7Zkb6EsgkDpbtcz4A4wMLiz0gucs0SsYawFSIJLsNOK6GNMrF3EVQaCV8Eh8mqtlL7FPk83E/gokgJSztshj0i4TIDPXne6CJ9IQeaSeqdJMdItRTaX1IqKicJTtJrZh0g44Bzft3e4Ji/iOcJOylQTDxT7PZ8L5BWQP8wnH0+zoLeKqkYru9mbi84qwPQxm9uAwt9HmfedmUfjEaIs6eF4T1HY+w4s/uQAogvgezlpBTDY0bM2+f7INb9iOJlgRk1vKngXxXCZ0AaMtUwvE1H8/XeQzO0IU+DuAZrYwTJrDwxCdIUNR6dYzkpT06sSTx+D/YtZnaBh+PlzCwrwgyWa2hpRY+FEZDvCaexxAq9YGQw0yDyAECZpI867mWMVCv5bcl8QPEkGvcSsBak9tePP7KfRte+wjTsRXGJKYQ040ldZtQ11L/jh/KLNA1M1nkpAHDfDeTwA/lPEvlzl4GI81WVuUuKSahagzrwFLF2/UTTW5LVUC3U3GWEgS+OaQJwTzz/OIiykmmsTSDdukXfLdF8aU1tIBN04Zz9Rnl5tdb8MRFAe+2bl9uxKG4FGvCiNkxji0YNiHqDTH3JO55d0GIC6oUDZmXHnQYIxk4uGJ8TbLGyoddxAZosvFlT7R7dMJnrrGg7pnRYCqcZXRdsBgan8cInqk0pTAJmP/nivRprShfboHF/yO5JM0E3dcn76gEywLOkTaurudYcse+VaueJzlttufb8EkVQ6Pf4ol78rOHHo4RwO+vnbrynFeJaXf41MPaSnm6g6UfRSVEAU5rwwTCS+Nxp6RYVaBmAo7CWc7KXFwaVAEYAw==", "page_age": "December 25, 2025" }, { "type": "web_search_result", "title": "San Francisco Bay Area weather forecast – NBC Bay Area", "url": "https://www.nbcbayarea.com/weather/", "encrypted_content": "ErkECioIDBgCIiQ1NTc2NjExZi1hMWVlLTQyN2MtOTgwMC0yOThkOTU3OTg5OWMSDBX5Q7y6nq6ceJ/NuhoMR/m56PNqPnBMjckGIjAK/WIzf7Nqq10VgLuM7sSsFUzLv6hKIL6D+mdk2BBNjLpLsrt7VwcFgadVJmfsOQgqvANVuCtMr1AF5NBN07z7HTXw5z2+2HSOm7wbbbNiv7ndM4XmagDt0KLoIG0bcp8CqNGTfAOtJbOIXhzwAgs/Vvt8WlW4k86RUBYhxlbinX5kPEQgKMkKqB5WYy+3KFEMFYwM3+l+peD0ZgnvJaedcbfj+szOBY2GuHY/Ar0M6BkxexKfLm/+RVp/nGvJ0O3a2fiQuJQP+PP6MoKXL5/SN2j00AiXFexurm5FAfpDkkVT8ok0lbuMeviRtIOZ46MK364pXhUHPCcq9MVy83DWmW6YpvduX0LUXDPdpnxaauUwFVzRz2rtXBG1rwRKM4hffzZvxeeE/6xqFhb3QGd1EHeu4eRo5BCJhx186V9fzJnIQIG5lZdlFARapHe/9bO7r0sWtUcCSr3k+V2getI9xYQkhR5w3CJGUG5nM/xErJS9RMyJ28V+Fl3h4WNQZcoSW2h+Lk5sKzc0hc7iC5ibXVDTeZdM9FmGUBaplvdRwITC92z+YCz8HtM4UaQ9pVvOgFAW+ffx2tlsrakB2+XYbiZHevg1pyEDkCjT9b0yUfaPMWnQOtvDZ6eKuWg6pQxnqlWrHhnkhjGcW0RzZrYYAw==", "page_age": "5 hours ago" }, { "type": "web_search_result", "title": "Live Doppler 7 | Bay Area Weather News - ABC7 San Francisco", "url": "https://abc7news.com/weather/", "encrypted_content": "Ev8XCioIDBgCIiQ1NTc2NjExZi1hMWVlLTQyN2MtOTgwMC0yOThkOTU3OTg5OWMSDFJQ0NVsNXLea0k6HhoM5Qw/m8oAgGL+gU1vIjB2M+FsVpc8J2DU/QxVExiMpWJPm66WqO/8ysa2s3cZwqiB8Po05d6Eh5qN/GURfocqghfAjagWRs+UnAGV9oFNrRGFKK6cOdsrMzQJrO7TJlZxRWWt6ELB1tbbv4XTUIgrXPkt1uszLn0pwGwIfcl6LUwCzfR/N6rsE1SjDHGKydGnT0TKO1tA9Olv5XcLzw6B5iUDx8HnP4+RjFTIRq1KDY+zDly0bF1aDpt0O4PZahrNJ3NdUEVvwmxyvZztIApxWO9BE1GMkGg/CnSgvmOpyDE5rHWYpENFmmEqf+FKp1UqdrANqD331/X4Yta1TIb6TFRbzblxh3n2JutiE5OvoMrnJm9Mh8fCz204Z39Qs1fq/mhEPKLRlx40/p8aMIFB+MQCR7/+MKvHDRYeId3BQd8ZHYJu/O1VMq5g9rnFQm6uaeqYo7oTUF++L/JxNLEYBsGuCoXQ9R30dQUlmeeX4m2ru/qH+hl2WGKnES2x1HozcB5HdutFg+ai5RilVOcTnwUPff3fbJYE72CjqCKUTdxt81e2VhTnQIN79BMtYQ6EPTeNEzCgjfiG0J8v5zdHoXrdNr2HXX8I4+E/vABstxINzH9KZO9FsMJvA0N2ffEaBt//Buw0PDSdE7krOXy8nVWHnHO64MbZklODDJd5Y6r6r6o7bnuZvWDGc0PJXaIrP1gzyYetHJQwlfAp37XkxDqcPOo7w5hKi8h0xHkzRPL8TVw6Uz2A5yxq6qrNtoHUuCojsXF9vmgUZej/g0jDhzEruUG+yRC9++NWLnV1D3X2YPM6sSL8xxc8uAHOTJBrwMbQlk5zmA0+6Cgo2nW+l6CHEelipsaojldEGTqs4vWwMsEQQXy+kFLZmnNCa7w0NcgFXbTNwRsqFpCvN0v2F5b69a5MiE9h6haF+M2Z9oIwIXpitV2Iz4yt5DZrJrVDRe3WzXf1IoNTWOFPaHmGuuklvdUxkk8caiIfMLcObZe5xBkoJRqYsV5Eqv6ETgZonU3+8cKEy+n4N5kPSkibsntBiPWdehpNAwShfXyCn+8Qiu/uSFrUC/5YFy0j7fp1ISepWef6YwTqbSpB+s9GRHMEnVR6GNd8QCiM2JNvBifN7DnkodcBAZ3egNXVh2eSUP2gft52suPFMmp1sqe0BFEoYSBmtjDXBuSQlBfnCVyS7MAaLSvgRrcNmyGluQVoD7OeadRnMYWBE8BVde8SKliiZWMHBHUp8KMdysLFIe7LK0KEnHZn2Lnlq1H+pwSGyaKdFzivlg650xPluswirhPlhEARElM2MNLR5kpZF/mBuLGgw1yeTt/LX3t0UXJeMvEzq5ujCHqnuLBVSYGAJRhbM/u4NFZvuIpnyDiy0e2QSSCrQy9pX5set1bIg5fveP7R1YWALtA2ZoyEZvAl5Zo7s/xQAkcw4bhL7ZDYj5TfU7iXGON/FKwyUPSocUEuazsw6+bhFenzpm+bRoi/r1st6Y8EaF/APskcX6+IOJIsB2DvPB5qWsyrzMT9Fp2meGD+/2UlfFeIozg4XOeXNm4QKJEX/UpED2eXprfuk5TJFTUODsqFs1vvYZEdTIJjJ2QgvhPTbYh7gtGtH80KNenPvjoKoEJOCn2GKjm9jqh9+6SOjp/pUSHGTbFm6xsYrYw8puHf9wc2nFBEKl+qcxW9JfZhzGofN0E41bUnUOVRU4qvGXysaLpE4QVXEBcvzA87hDDIJmoChfhctlUoXO31gwRUjbZvBGQXU1ofPp16y+z2h/eEruOlMSWrejyP3x0Bu6uF41+WvNqG0UsiuQYMph05UZvZ1JQ6Aihlt4LYUG2dPBN7xINlLxRodmUeAoMqexsHJtNBhjOgYG7VgJWa3VyP8sUnXm7sKETK921GZQoVJOLmH3FpgRxAuQ7tNAIlLUN+rpC8IeNwu1m2sZwHqV+TZzr5aIXQCtxapde2mGiBWi0wMFPGGdBmLAQ7NEGur1n4PkZ/PbR19e5fLbyCkqLgWp2dQGNwAzyVtdPgeKG2GOek1uVabtiT72HAx1AmJX7/ecXHMRM4tgmV5hgqka/NaAy+gNfg13hW2hJ/60DgrEQELvWEFxENxDhsWF0vJ0ZOqHgVJ4aVVO8pUgwkhy1JtsrhYTDO6kg5/xCvALE9iKtkJEnQ83cM0M4q+kKTow6/yAy9U9YM6UdVVkHh/BysSrO0D4Q30Dpbgpd+7Sg3bbCW1KWA29ir1n/ioX8cUvB5CF1d4M7qlWCUsNgY9+HDZSiyayyYPe4Zq7AuxRdSbNPF+CCEXBLLYJNF71D714I3RPLWCI7JibO1sFc0Zwge2W8ZYLX0u6yZS8FXMtil1a39LMKcBPO3L6jd9vD202I7OVtYqpOX4oCE3PqFASbSLzirlQu8qa0g5Naj1EiQD5EuMloweRYrlFhYo076eRs2afznfTJkC59zC7/xjUdQzKsuVk4cXbqYp4upRd52Xr+8VjrTdQxIocXE5do45FuSA9hGgNvQ9kI11P08axvb353pIxcqNClb1DwfvEVcRHGHsteooI/AoM8vxV3y8KWnw4V2BOS4RPQSEymv1y/4WsIWx4Zq+7VWxXNmh1jqXI1tIQqlP5Q67ShGLRy9DnWAtFG26D0+kRTXwOoOyayqOCBM4xq3YWe+3D+zvxTBPhT8TgYRskC4T0SC2bxZT7YxfreajZZvAxd5hGxwI3ELEHXvEBgohXCOFV4HhKnLKw+XGwlbLqKzBwxM9s9hFWnZJ5g06SVlQaRaIdTzBMfU6LeXh8hJspcjwPt1nhbZyCWbbHoQ0wH0Z6NXA5YViaGQojvKGlicBIpm+Q5VaUYFTQ+uvSvgXe0HyKMxlJHV9u4/Pe5lutlsvTUitXjDPNl1/M7fHT7LCRGgZzw8X6wGO+QYhNOQyTug+yZE2+pt9GYqL5tltwUsJdPnufZr2bA0wccKrgpE0hPa/OcTsbc0HtyNxlWSmRtxeLIuXPFTl8ar58qrIBho4g9xcvJo4oap0YPHUtD7aMnGdT/nx2ngW2uns9TZNV26zqQ+JhRvJt/bczgfuR9iMVDoIfyr6c3U/yJsa23HfWE3CV/h0qbGW4P1sAP/gMOuULyJCmYTMKmIngQWWwC8TSCDrpFirfj9QT8iaiQRcMseYdj3yVJgtFyqyavqWeZOGTKuECmZmg133Fo5597ahZ5q/24XEg9ToBhwb1YDdu2AuSAPNGAFM/BjADkzY6cIPpYSC2j5E9bWQa+BcHM4AySMCSx7QcP7qy2foAMb+jRVdk56Hw0oNdmCXQ5MaRsRRrn2ASAB+SoycMwioSqmbjx0XGMfQvLGL0ey3byeqmS4wTGm7Cp9bT7up1wQkVD7qKHx3aWGxdZtEFswdymrPvFCCyaFoNEjt7AaaoA1ClFxYMCiSqT+ttnyIfnjC63Mtucu3l49lChr1Ep3GsoJ//ZKGoR4APD9RI+QlkOqRpU9A7xt/SKLoL6dz/7kAq6YPxqk0+Neb+RzGnw8RkJ8YyLZ6pvA0g0obO9+OEBddbTtQzuFu15VnHUGktY1HBQYtoe95Dx32zvyzCI9L5Iik9pZgaFzrVp/qAJPjvOwkw/VPXxcCgPfvkkyf9CnafWUWf0eo0ZGyxaWaTDty4oAVg2CDPdk1FzdvTuVKjftCxjuYuwzSAKpv8x1c8xEJjdZFOkO1m40ZTt60XZpeyQiNysKolXhZYkjE1pb/aksqxf6ZQQQ5qNmUJowV/0s+HmQ4ovVydy5SH1oiwR9cHZC0FltkSpOWFntEZEpAJNqLfYhp/ZRnO7YO1SmRo/OdevfF0+CUy7istf83HZjb3MQFDya4SOCAVIlfUpRd4a7mhCygvEASjHEbalSXboSbcmYUKZ2NYWA7+uSD3M888WK3ABdPz++RFJaOVdIDG++wELl5MmFuzA3JP7SilccTL7iQkNspht8oQ2a7Aq6wxv61e6safzarWCIsER0jo74NTecrDwrVOshN39QL+IghfpexjF6T+AYAw==", "page_age": "9 hours ago" }, { "type": "web_search_result", "title": "San Francisco Bay Area, CA", "url": "https://www.weather.gov/mtr/", "encrypted_content": "EtQPCioIDBgCIiQ1NTc2NjExZi1hMWVlLTQyN2MtOTgwMC0yOThkOTU3OTg5OWMSDBN9RDDHmJ5GuVotMBoMzy6XmXJtjI2lRrQrIjAv1Ob/qiB6REeilcFfHQr9b/ilqnWc4m+UIbzz1LYBHYDK2fo3rUwzH2inyoCfA8gq1w43r/hEJsBLNIKJhpv3nsxa2r5AkJ2byD7o4WntRz++HqnTcUNdoGYqvh5FZZvYTFI+w0f2gMyrAY+TNihobSOk5u5oLFiJFkisWF4TJmvzqbXEsFli0R//kGHD5MAPUs7gwsg0kXqRWaoGFWuD+t9vGUyAOV3C1Zw+DaWS/JjM/uUTFHyDgDYwUcv5vieZRBjk8nau1sgG6g26icFkRws0ThzvDMnNT7d6/f05vzFpVNSoDupIdyf5IZuXX1HVEcuXKpl6fZ6dAQCFO5GaabHDCLZenrbOxR97FiwGLosnK2apmDVgR2jnzfrxqEE4rBjbmZkO3EtptRkT+4a3pUh5FBYNttk1mrqowap25fT4yKUe2F0leftiZ4lI6IOth2sOgihfuSQ9odSQZWYxJfHoT8uP0pXYjuyuUKNtwqYzLDdHtRVwhwFVJb7Y9Gk0K/I4ZZfKDLK2exg51dcmdwoGiwzrT5h+sldEiQjiB+Ea//SzFBd2/Krpwb+iflmXb74Y0cHC8z1FiVmaOhmnAjhiMV2qMUCnjzDa4J10JrelaVpET29dvwUxliCm55TSD6sNFnAlYgtu10nQ6DcOYSX+Hdy3DQ61mMb4FOiSNMwFzVJi7/YM/J9P+PlFAYuB8EQg7zeJLaOlY1IcV6QlRgZfy0HzIQU2x7M3aPGAt3jEgQ9KeiAeEkxFKOyaGIN0Hw59C9e05YQchXkGkboUfJcyI5i+AIHWewBkG5WoeQg8cYDucRwMGFv+wn0YAEDC1cp7Kx/MaQijGpJyYpXFwbjQc8hvg24CL5CU8Y3Rj/HxC6OnNewoO+EQttlNOXaFTjilB/Y0/7U8ASlGbcE+uL4uDTi59oMv3pvlHm8FOSCtXGOT345Rc921Ec7OEgjhkRtqbQFB9hiFzOzN9ZYWgAsV14HPxH8idFGzGlhTBH5C9qbGRAkT1qJtCcrPCPRlHs51oXZ7w5PS39pAEsS4weZQtgGOB8cSDvkI+cfOKbDro+5vCEgvAvspVcLdPE5Wu/ZLJC49z552nHlyvDlrXCxPY6WaZPPF80ShbZZ8meS2+4dM9osicJZypjBnmwqwPjIe3aiPB2e+A2ipdKLR9mO5+b//CG3cqNGZfzJDrV6gYXXioEULgTnLv7q1KV1jhsEABEIPagcBcAc4Ny/DjnnTgLRrBxNAuOFMFNPGIFxzGvx5YjllEHA+1fFaisIF3L0WfOPh6pzfa0VuRE01MF6c2UGlwdDy2lI4higNf5TZ6wM9OWHNX6r3H1YDx9zVyZcYZYtT/D9n/aMndrQUE8sIF0Fgd6Xidcj1+mh3hurxsUtdn5z7kFYGb3KofC32YTADywc5huXfR/tINLy+Hpav/hO1xVoRnCUsPY5Gq9f8nmpOo2BWPhftqQYqXSTuU2195haLKPXou1aVdhb/wXg02xsZ9KPkhIhO648rGmn2ZvQcXHARPPmr5L6CUGrE+Ni0C8elE5m0zkWmmKwZZBdoUCIOp9qOnE1dWXccyCAUFA5Km+bI82mEnKPvEF/E2jdLsCSNF5EqyWIyw4UfqDy5e8fvs9gJJJ3o7a6XidBkxrUOAA+FL1qR+1aWqSqa1Dz0IrknmBQkuA9yJZS2DWFKyvO0SEYU+KYlHHffSCb6WTBXszY9fSDT8DQtT63cHTx27tpre5yAdrgln1F5gxaTMWJyPoDsDAKg2Swi2gkk7h4joFh5bDIhMdjhH7SDyERF9QoXx1xsuUiLrQp+bNjVO45iNsq/yXJS2f7LeDUOfSitkQHiaMLROMD9jNhqMFTbYduWHWAvnHeUcOjj0O1SJZ5MVbrGNCFf5STMEYvI917CCFCTPQmrH3klNHkgn4E4Y2OYCV8kHMQsWHjd/v9c7A6tTnqT6Dyee/PSWX8M8AC6dBN1nMk7VgeMQIAQpKb4ltBI+b/+Vorf2G9jFQtsRdhSaLbkXPGmsRfFV0NIr6sVVo3OEf71Djf5Dc1KfUFR/YpHKZICQdODFSqbQneoCAYLnbbExc9SDon0mw9HbIcpuMietsxNdb0sk3ndX9xTFiXxZTaNbU6oBePsfZS9Efw8io0cebIc1whYADOGuUdC42KPAHzyPI7KFjIaeAffDQYi2ue+RR+mVXQ54y50AlRwuLvXomdUYWyVAoC90SCtAYhR2Nwvcty2vSMg0rveLBnMfPkoWXEJoxKZUB36Kgbjp0jdDQpIyJ6DuympiW5i1AMJQIi4JmHxKJ+kbKz7mN1F8BIRA4OiS643nBkZAsIPBw7IDf9JEibwKouCPRa+7C4G/WWdfBPmPBb3626IBGBN+NsyUgrJpMM/O3GCvym1UX8Ify0ZKmeoyhPRkYx4fQi/faMZRGmEdNAVG81zGZS/i+PoC5Yd3E/2aHTHkHz+9QetnREwLbEiJupZgUkmN1CCadrLP8gDAM2FsOdMhq7oY7Bebu7dHBy97lEHtUDS/fzuhsXNlofWtYXw3gkoNeudyMtnuehgNInbbFYyxjfcFfuVGAM=", "page_age": "5 hours ago" }, { "type": "web_search_result", "title": "San Francisco, CA Current Weather - The Weather Network", "url": "https://www.theweathernetwork.com/en/city/us/california/san-francisco/current?_guid_iss_=1", "encrypted_content": "Eu8HCioIDBgCIiQ1NTc2NjExZi1hMWVlLTQyN2MtOTgwMC0yOThkOTU3OTg5OWMSDOomgHdsjGtvW62NixoMHXcF63es2xmKeaGOIjDcvFRcoK2RHPSfHSFwcCqjDhDyjnqNFjqOkOsq9DkQOKGjRgLd+fJwuoUxpwIzq/cq8gYh5k/3LKqXCHgHwGwr32ndAiyc1Clupsxlf1NsXevdfmx0w0E5yoTzPHsXehL+QoJlw0uuL8g8gFhy03Fqe8qtwbviA3kBjVCoELK8QfwKSnGbhYbePcmA9uxZ4J0I7exosidJOc5XLierJeu7gjNi8ugOmOgA0w+8DComo5ctM7dqCpHZfRdl8qSAPPPPfFLD69DibwQrScT99LLe8cXi3sjqNIX23EhCW72+lPBQjudK2rcgMGI1UmV9j0wfEN39xiNdwcN28U9fMwvaH8OkNH21rA3W6cVpIbBg8MMbyO6NjHtP1TcGdXIekDRKTDK+GsrcXnrvY6c6uvGP9VAv4D3i2se27Y/63hXUDkrWMi8sZN+jvMk8q6XhBmx7+fv0OR5fAdiSwIMM1G7W3BoNArLyKGXfJuWdT6uKaaPj0DXyfFVYbL+2SkqMmuu+tzLZyMbnbFTVOq27DttMWnzb/rWsIRCWLIYot8yiiTsSoKe3yVI+h6x9BrzRUHSS110W10J5ASa2+ICiHhRsyyNkCOryjdyQnrOhqXxlqVFu5q8HAXb+FvARXVnnLDCpDXvfIv1g820U3/tTm5HqPcaV16Qg6l7fzmZ6KUiWq+/Z/O9/JcM3O/1Y+VM4lkqXrkKCkPFC1K8Pc8jKGVe9JNmMLCCA3/JS61UDiZLyxFF+HDZiKB2lg2tmzMDqJqZLMnHK+mUk7dNdrs/dfGP5reMeSRsAr+JNPOvg2txDM4flCCkGLoYKBQPssunjnDftGEsRUiqGz1bfcP7hisiNImV199pGJVzjcAP3Ati8IQY7HvqWGow8B4otfY/d9sZW1imRIJVHBeER7O69nuFJ5Orv/Dt+ytRKzX2VMz7FG4RGY+17/gEJrOZAV4oI0CUWmmwmBxmPQI/3S2tYJb1kPuod8s3KozrfHI+EMw/juMrrvAY7XfwoMekHs6elVw3okY0B9oiIMmZqjL7Lo2k+Fx2wHSMRyl95+EXiPQLGLP/dNEmcwkI33IlekZbmMOei9ht0Lo8reI+6Ca/PiA/5fil5t1dccUkF2ioXKvvcACT4q80N857dEs39/cvubkeQpzN2P0yqoniYj2mG1HTb+SjxoUCKst+1V+Q1/Vhw9Nk3HhcUx+bnszO/nXdgfhZJAD4MApu2PH8WiODtd0SESCwKrngYAw==", "page_age": "January 14, 2026" }, { "type": "web_search_result", "title": "National Weather Service", "url": "https://forecast.weather.gov/MapClick.php?lat=37.7771&lon=-122.4196", "encrypted_content": "ErgBCioIDBgCIiQ1NTc2NjExZi1hMWVlLTQyN2MtOTgwMC0yOThkOTU3OTg5OWMSDJ4IJnqFeipHa0k1uxoMvboTmPYYZmXTfIMEIjCs42QQJlkM2JDVds66kQFe711zy3qfx3xZ0bfeNIKGWIcqb90jBateRIOnqrig4MIqPFWWPfav+rerkW9+Qas/0GLMn4UDVYQRqoTqoD7L4Djy/JA0s3f3IxErmXZPmjwlk2MJkZY5MPY3bwZ8dhgD", "page_age": "2 days ago" }, { "type": "web_search_result", "title": "San Francisco, CA Weather Forecast, Conditions, and Maps – Yahoo Weather", "url": "https://weather.yahoo.com/us/ca/san-francisco", "encrypted_content": "EugHCioIDBgCIiQ1NTc2NjExZi1hMWVlLTQyN2MtOTgwMC0yOThkOTU3OTg5OWMSDBat3wWcoufUkBHL9xoMCSpJaOflargX0IrbIjDVRWbXPfkoKT0nIk5yuLYz67txmy+oplZF6cL4tSlQ1MCB6o41WG5XPz/Drqkm8aYq6wZ1JLSeZUOyNIYgzx2xuszMMu+GQtrI10PoKt79RNOWIsGXBu9KSW63NGTZELzwps5R2e/0G5a2bnqno4+TXiDOYWjjkYSY0uoRZ+3yQZypoj9nmXQjHqkqYXbAoahZFrzUAskS5NEM6m6vtXXFDNr+RPO9Ul2Yn/ICP100kJP3olnB/9IxzYwKvrxO3TmMT2qg6bD7KSF7VlaZQ2FIrc5vJQdU542WEECgCTLWXt6Yn6GXBJd6VrK+qYYou9KIAa12onbXdEEovNZnRAacw7BxPMe6fuT26mnx6t7hv7YwnOrVo0vvzXxga9ZLIXNLuzkemKSEpE9glUew+IAIr+2g3Om7Ax+n9GEDhhQRlKJAFrbf04c4aqLjcXcdXho36kKJRw91Y/TxIdRFSQOSSdvxrqUV5PWMRZPBUa8l/6ej7s9AqXLkraHrW7Rjn3FqYHW6zL13vwZMJZnAvkPmd9nrSZjEkneZjEI4Jv6EFPq1ike1e4fM7GaAc7MEsSiHSAE2nB1ImHAS461aaEmzwaTRXqb0tvxVm5BDssQzsSrJ8EAp3AGYnqfj0sfoAv+3EE3XTReyjOQadCgQOly81i105Ey8fRACdCvTWD90JfoiS2p59oUssECrmG4gRGzCRCoGsMdYpUAsppMfEBykfvLhJWRkCHQ7aUxp11lmyCsy0qpSlSyUmKkFzLDq9oasphlPbu9Xm9Wr1IYYtJa9vWhMdeo9oTmZb91r9sFqHqMeSzSep1/bzs6B3cejXDUxjRV4mL8yUk8rFTFpcXS7XK51XvvbrqiL9dtWuI0raxktYHGcI77eVA8Xj4p1xKqX7GXWSZTp4hDdxUNA4nDR4RANdJIk7r7whQBQJsadml5H+fY9ULhMu0oKDqbxhs8m3Tbrq7qSuI3E2Zu4z4GC+ZzypsTrxguIJoRyLyxCltGlfXdeQmhRNcueTTvhPjUl+Y/8ykTWwi3lnl6UOVHTM2bJ0ZTjRSqFsPAEqpF6JArzbbgAvtas3ShaJJOwarCoXTDdzuRF3/pAM9FiHntKWR2w+p2Dbez/dlmzKF1kAPiGIH9ocBovZXt2f+wq0BlswdlVpRGxQ+hQe4UHU7CLZXEBc0zhpFYyBJhW/rLNkM5x0D072fiPzEtnWg7duXJDeInnUB+d3GL6co6wGRgD", "page_age": "December 26, 2025" }, { "type": "web_search_result", "title": "San Francisco Bay Area weather and First Alert Weather forecasts - CBS San Francisco", "url": "https://www.cbsnews.com/sanfrancisco/weather/", "encrypted_content": "EugYCioIDBgCIiQ1NTc2NjExZi1hMWVlLTQyN2MtOTgwMC0yOThkOTU3OTg5OWMSDJva0cbXeTb/O42HrRoMjplyYZxZWXk7PUnEIjApsjOxo9gn76wWFWXReI2hbQ/T9n6HoD1qeOzFAmnwo1SG1pkQte+6fs0YRb6nxuMq6xdhYh2GeXTpiQkam/FT67Op8QwFr3/I38i2usyTzCjIQh7sWxW1SrUnwChdGCI5QCVvYgL4fA2MoTa7U1Nyhzme4CFH8acLKLCv7MhWK/cu0+IEmgfAaCBN2dm/ETTXEYbPIBxzRi5a86amcI0r08GpgOzZC7MKJT5Ri2uTX7YU9TS50TCVAXsRcbqTmmgVeY4JMJVUioNsGej/PWJuIypWNub26itmuM9VRxwec0nmJHOsMhSVeDhMzLnLITQo8NBuBcMAJSRDzQBKeM4e/q4Wfxh38txldBiVwPAl9jU2NROCi0/QHfiqXJUHX6b7uSu4PnlEMFEpCoEnr+NSotSGqKg2ZXuD64FOB1kLgUFQCw+mYsgdJks8dOidcydi9HFKF2AuxE1OcohIUfJ910XYc+eN4tsDwT5T6rnWOqtkZaj73YURaBH0DKisH7VXbm6jJgfmI8Rxi5jU7clUq9TU/t1tVU/rCaEgylLOPak33NhA03e6/h6aW6P2TZI4qObaA8/u3XcmAp9jySCi6yvCEc3n7yjvDmXSVFuF2fbSXOnnXCzk5CJKhjuIl9clwW48vIvvOWLAyDK35bc06cmN/GhzjDQOhi5X9uUernKyN8AAAaUV6IwZw4IsCA36B4L8KPg/uxaZqkkA5pS60Mg6RU3FTJWaiprtZCMtWcbXkoy6tH0wYnmAYbvkVqvzUsm+dMU0uAgR1f5kdbtD23B8sk8E6IrQDI2E4xRWbKcN1Wv2qlIMkTCAR6iGezyc5JnZBIQ6G88rQ+hn5xWs11h6Vr+rOGyBgNJGFB5iWO4O+Ef/20CGQ4SEMiiRF0TJ0t5FGcrU7gJOXekLHRImOdtD5yG2b/ZJx7bwQBw54dNmITcVwkST+jNURj8wEW8xZedFbCDupduwewRMFTSDoei0Vu8MpH7Peg0dIXBMdp7J8ou03zYktyM+bLjvFL6j7yatNBW/PTwbml7tFyW1LxWQiTNGE/8nIJ/Tec52K+ywIA+pwG81aJHd0QHpH86ilAlaDIcDGdWDjEcrACovosLRTiA9Sdm+fgarFPMiHc/J5j3IASo5CvXStua91Kb52W3218rAvzUc6fQtchoGkjR8qzbnikPjzpsKpw6GDT/KxaNRNXsg6hsG+TxcHMjWc3FA1KrKhTfLO7ue55V54nTZmTt20ZO4XGeE34VoRzdFRFybeZPul8BnzyA8o3aOJ3uCgfoAHehosf3sma5+XW9joLhwbUm3rlXWBKOharWbVGOUiGu4u5udOnJDf6VBxrWZ7MZCCHtWI8EiWK7M7qfeRefz/qhPUWQbCPJvZ+bs0Y8e48p7o+H4+HEqSTntpJ7Zdaq2/C8gxKHnoAeTG2Nm5RwwhcgAglczF517haPn1N80HQDnXzLEf73nxPPy3FSHgwAxA9RpN8kpPVgH/lmpVxRdyH1mdXFtC8NavJpDojqlh32c6/YTNzgNpC9ecjn5ZyC+Qur+zDsmZ6vGprY+lip5CUKPbwUWa1/ir0l7VFhXvS3eElXS8tcuhRSjW9GsmttGmCkDVFf/9dW/FYPoP0GEhy/Fu1MIL927EPRgfYHQTpHEkdntcgrKGEXGeCr9aRNLunhxmGTkPT+PQ1vvLTNBM+6LoI1sebUyXVI2oc+EIJ0GuXK9lG2U3cZ5dKV4zZs6e/o8ggTH4cwDVzctoepSKWL3U43gB68z1+XKtQ+VA0KN/t8oXU4/snJID+QPX2K1p9aZcnbIlEosDntRJEv6eFmZtZFQQGog50TaB8Ap+TfenPtNTehmvJXKQuy8xQJ9ADXPiAN2+b02mMFj+yNGR0UIb1M7m1JYBZLv4kGIIb5JgAzn8gpXc+fh+cXQQNO4xrrigiMUiYTZUrT+H+tFcwZVOECXjwUHdlM8rMvib2mLqmEPWhPknkWvEDr4I+XzB9WSINalxiC1lGh/AcMaNr+UuPiStamUS1heVV9/A5FWk/r8EzuFII8NF4kcNGK/ejEfcgSkOTHXOr4Qs4YDDaLcqxq/IJCNqze0bEyYu7KeGPWwTL2gZ53HMfRkAO3GilEi4r8Jm4dEolc85OkShLkJB0eoNV209wKvp20ot0kifw6RJczHVLQ8Q7DQ+aSDIm0Jjl8129I15OgA0IFdSRMkyi3bhY22iEtMV+L8m7JIg2cJ5vyvGPkBY4cDmivg6RpRjeGFsfBhlBKfmyK/rxbSkLPLQXq2+Qd5xcTaEyyAhvWuhJTfwvkhBbp4SsUj82PWKU+SaDMCXoncBekMfEBLsrpFdkyUh8lgBysj6LN2S2yfNWuYwb0jSc4THGNRuprIvqsFPELNcLjYQLU8NYQh2gvL+5CRrmZVV/nwf88i0M+5V8IRXJ/94bwVwZVqHFfnMy8ofGT9kRXqzkZvMeZorLYQysuO//qNoBUsXriNzCoK2GWa8bXu2LRNOEXzJqbZuuIrOK99OYpq3OouPd768Hw96Kldlfk222lOyfQyT8xVZuGiiQwJGSsND4fDzgXaqkz378+T8tsZxoSIV8+13tHH8CJjwWXA25APXLCUzkC3iSaLhoy6XeWuglSa5dailrt26qraVA2sZ96QADfoYV7h+EyGkVNTOSKzetff+IxtUlLR6N7yq2OuiaRXGEqbuQsYFOt5v9IbuDqw4kbfghPSsM8yJj91YZX7/nURfJBEcZyGqj9J0PRwFQSHl3yM2xHv64sii2oCrAGD8/whKv5rrZQTd9uUK/0pDQm5BvMW4BbiyCYDN7hDntdc4R//31Q5TcPmhnKH7SsK5vP+s5yt9F2WmYXPwaJ7VoeHVSjJ6CinhMbIgIYYOWDzT2YUlQCz6toS88429dkN8Wc5w7QyyJtw7Rn+KtqkqWwYn6u6KpOfVMPOwjKF/DBaZi78Lqs7Uc7eqi3zbwkvc0Ynov8klgz8E9gPEWyDH5Uq/47cXgLx/zXguPRAClPk/LHeiiFmXu68+anxEfpzNgOYaYGvMLKu7PHuNd8hQcXZRwiExPtJ9zzc1LP7QGDfq8Lt5IMb/5Op631iufKBfQBZ71jVSo97vH37aX4J75Q0JKsnbyKwSSYDDUjVbud3fFwJDAuNFIwb6E7og7KRoPbs7LVifO2hxX8PPeYeP3Jn6C3GzSATCGAwFZGNwdEuylvUXglVqW3AMe3iulcAePMLlIEOMO137q0QKvzaNoD3/D6TzfNJmIswliJJoJ2vp6o6XwFJqtv0/7dY+zQozitM3DReapuLbiGppAomclvR1n+bwffsGtzC14RpdtrmM+CKi05Bxo4ROS3do1oCdHhGAc3MXEjHMS+x9wJQ1ZpUbT0z1YAkcJ/bTkDQCFrXGPFaDMm3Ksu9RVAP7vN7LGMseZrS38f/R5fcK3DEmmFILJsMtEA9hMoxQ3naCXOeMCDdiDbpfEfg11RbeEajI48XZ8JRhmeXZ23k12W00KSzd3sf2qDAXkSZXNmmxnK/LSs9xua798uUEK5IvhM/5afQAyrgmRJ7OlwXZ/EejNEMA3PozwcYWQ1yWO7Z8J7LGHB5NIuIz4o9nd7A/a9rmoEA72sgn+Mtn98YkYIOSlK35LF9LCIiib2yb/BOsol5ezEu1nZDPCGtLRIvxy/aVtKzz/3g0HpbgNrgkBtEkfNJpQm6aSE5LtXvkqImpR33OzSVXaMIcn/8rEIVZkuvcqs3E0BAm4oCq/X/7xQ9jxE7ntXspw9y02efnvirGHUO6lLFvF5n1AW/mUW/3uf8rMUSAs+UBZTOWVM2axdXEER7KpfG/mAQtvEcM4XAcHeuEKUcWz7U9UGm7N3QQ+OMQNnKl9wYe1sv9kr8Ch/qswIZ4KCCnwxkt+uQGI0ZCqGoPqkOycee1fuKRgH73EvnnGzHUE3qwSeyxTAZNXr5OpPA/4fE585qoTnJFTbreZypPYobeCy+Z+uaY9BGKxbr+kL80OnCSqPasl8QmmENfE0XC6blcFQyam9tnxdwTOStJJiySRha59li0seqk7wmyPuGaCz+HqlFQKXPa5b5+ctqDcZCryzihhAS7E8I+jIslq1hM46MGhfHcrGTN6BbPEOuIHM7snAYAw==", "page_age": "2 days ago" }, { "type": "web_search_result", "title": "San Francisco, CA 10-Day Weather Forecast | Weather Underground", "url": "https://www.wunderground.com/forecast/us/ca/san-francisco", "encrypted_content": "EtEKCioIDBgCIiQ1NTc2NjExZi1hMWVlLTQyN2MtOTgwMC0yOThkOTU3OTg5OWMSDOTrpPw+6wL4ly+RYxoMXpjZLAC8Rc1Y5Mt3IjCrVu/82IQK37YRqopUrEDPT7XHbhmpcLXIZfx4hsY4sai7CaH+IIuBOYaHowcGhSMq1AlhkFflViuQpk6W/JNanW1nUfhjwHlAv4/qZ4FmULkdVKjhSEz9x5opJBVkfBb56b6yKg0KV+Fvzybf5uc9ckX3SJpSnYRn32oEwabCDuZ8dyukcX/2ea3IRcET+NvAeBgA4ng4t6btnnN3GYcdJR5BB+PH0ZhmG/Lj3n28kjWO2TY6vg+S6np979Dr3So+9Jq7cZEUyaC98fVbOQ6Ae6Hohw34w0FoKeFtwl4McQlCxCs966qjJvyyxa2peQ0YVFFFGNSISttpcD2QWCMoJUkCDp09pG55CTUkR3WIOAbwQncfJtwAcEuDNZjC+nVk3ExH7MA3azsejgkgK5/MWcQuR5I+jwHU/GYnRz3QhFRGCbuCfMPg3E8UMPVk5QyFiRdDWatozoAGfIjVamyvCbxmhg7NQ2nGkpvuX/3M9o3GvJihNgWQA6vYvT5faZdkRPqH3LM7xYEIsKMulqI0xLVRdmAqCOP/nCX2il0rIUuKbxi/sqWu/FLznAjCD1uqC8ik/mYtvDDksNoTlwvzof1i2vlTaXs5S5XjKEATvZ/zV2q/7109xGb0LaW1dqi/9We96vrIxotMGCMS74owXNmTM+JqZIgLKJXaWSWlvUyjLEZ9KAXdtBDHh7FbYZbTzcvDdlsLY8MM2feAcOG6H91nMnPbjLbmnkNTnk+zFJKoa5NloZfi+tFRv3DMLm770dOjHwVv8kr3+iBDkwHZz7AV70WVV/yApjsxtai1Lu6WVVnbQZYYVNdtisIWQPhzP2AYOw6gMG0a/ArCMmSybjXhxe8V/7JRchOWQtxWz+qEamhmkFaqf+I3XS72I3IodqWFOdlBUNQwHON2uzg9QbKCi7EsVPABzTVSrJZUvQTG7J0EWykr5m2PZO6CRBlHzyw6RkXerswf59vMr2wKudwfJxGGg5veYnB33Xa0UUVOZB1DtDZ98bRr/TpBnjvmr20Xt8KjlYRUzVRjGUvuNXgL/h86TMvcWVWuWHelKk2BhMJ0jUuSLZhqtuvjTmYH6O3pa+Lf0yjAx/gi1TiduOwCw9HkRbOnxUhiaf3V1VaXkRWkRphnSeTmc8NuneggZHSwot88mLaJPgiWnIwZC2XeQ7JBOjgkEPgqEf8u8dzY6YadT7qOIqn8Xby6WM7gYUThQr5/egkN184tev1dzZjNVHLf+WLt605r7D/Vy5Hnz6e7dHXQkCFzS0wFgULQMijc79eX7MCGc5cLKTjj6xwWLbNwx6unm3WDZZenDTEg3TGI3RvtcWxglefrDg/uqzyaRAVhYnSuJGOhN4/h1VcLxsrlcT6ZGQVV59M9Kq6nsuajtoRm82SNqfw+C3mU1PoIYl9OHmsmtSRVQ90nhDgxeOHtC/HxqpPxoN/jH+rNFGPhtMvgRcReqc4M0ocl6SnzHnM0iKc9QBczFyiEGh8FX3jLEeVu08Iijhf9LoESy66n4xt+hGSVRIaIiJHf4GpglZ4/yX+2JSRVUhkbzD9tF5DSrG3FF0Fk+MdIu+I3GRIk8R2b6TT8nQRk5wxVYpoeklMZkf81CU8UujACu4+a+WD8BlfEOGYCLzV0yygchMnVEzCiE4rJFA7nQVf/310nHuLNP3WtJ1qfJLVC3v/S5iKThlwAxs/8N1X8jCnlAV9KjxoYAw==", "page_age": null } ], "caller": { "type": "direct" } }, { "citations": [ { "type": "web_search_result_location", "cited_text": "San Francisco, CA Weather Today ... showers; breezy and cool Hi: 52° · Tonight: Cloudy, becoming windier and chilly; a shower or two this evening foll...", "url": "https://www.accuweather.com/en/us/san-francisco/94103/weather-forecast/347629", "title": "San Francisco, CA Weather Forecast | AccuWeather", "encrypted_index": "Eo8BCioIDBgCIiQ1NTc2NjExZi1hMWVlLTQyN2MtOTgwMC0yOThkOTU3OTg5OWMSDFfQGC9pnw/GI+u4bxoMiYkRcL1cpbk2mnr5IjC9oELYuzZfyHX6RdSCxnKHSTgf440kkOxFmt7IC73xxoOukf1TfOWj/h3NBITkXWsqExI3HY/Y+Perp0YsGUIO8FR4v2cYBA==" } ], "type": "text", "text": "Today in San Francisco, there are showers with breezy and cool conditions, with a high of 52°F." }, { "type": "text", "text": " " }, { "citations": [ { "type": "web_search_result_location", "cited_text": "San Francisco, CA Weather Today WinterCast Local {stormName} Tracker Hourly Daily Radar MinuteCast® Monthly Air Quality Health & Activities ... Tonigh...", "url": "https://www.accuweather.com/en/us/san-francisco/94103/weather-forecast/347629", "title": "San Francisco, CA Weather Forecast | AccuWeather", "encrypted_index": "Eo8BCioIDBgCIiQ1NTc2NjExZi1hMWVlLTQyN2MtOTgwMC0yOThkOTU3OTg5OWMSDAmSyMsXr5yeQbuX7xoMrHFSEGK4rCQ09+LfIjCUwxaz2Sobj6VyLp3iNLDMQsSdBkBipKqw16rJP8Db9Hsj1KhjUznQoUdJGqVoqggqEyFovEymuDB+p/odTUAC5aFqgtAYBA==" } ], "type": "text", "text": "Tonight will be cloudy, becoming windier and chilly, with a shower or two this evening followed by heavy rain late, and watch for flooding on streets and poor drainage areas with a low of 45°F." } ], "stop_reason": "end_turn", "stop_sequence": null, "usage": { "input_tokens": 11306, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": { "ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0 }, "output_tokens": 163, "service_tier": "standard", "inference_geo": "not_available", "server_tool_use": { "web_search_requests": 1, "web_fetch_requests": 0 } } } } } ]555fb399-a54c-455b-9ac5-2c9673f18e12.json000066400000000000000000000266611523216435200403740ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools[ { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages?beta=true", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "NOT_GIVEN", "accept": "application/json", "content-type": "application/json", "user-agent": "Anthropic/Python 0.82.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.82.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.9.18", "x-stainless-async": "false", "anthropic-version": "2023-06-01", "x-stainless-helper-method": "stream", "x-stainless-stream-helper": "beta.messages", "x-stainless-helper": "BetaToolRunner", "x-stainless-retry-count": "0", "x-stainless-read-timeout": "600", "content-length": "602" }, "body": { "max_tokens": 1024, "messages": [ { "role": "user", "content": "What is the weather in SF?" } ], "model": "claude-haiku-4-5", "tools": [ { "name": "get_weather", "description": "Lookup the weather for a given city in either celsius or fahrenheit", "input_schema": { "additionalProperties": false, "properties": { "location": { "description": "The city and state, e.g. San Francisco, CA", "title": "Location", "type": "string" }, "units": { "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit", "enum": [ "c", "f" ], "title": "Units", "type": "string" } }, "required": [ "location", "units" ], "type": "object" } } ], "stream": true } }, "response": { "status_code": 200, "headers": { "content-type": "text/event-stream; charset=utf-8", "connection": "keep-alive", "cache-control": "no-cache", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "x-robots-tag": "none", "content-security-policy": "default-src 'none'; frame-ancestors 'none'", "cf-cache-status": "DYNAMIC" }, "body": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-haiku-4-5-20251001\",\"id\":\"msg_01AusY9WEbCaj3N7Tv5J4YjH\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":656,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":26,\"service_tier\":\"standard\",\"inference_geo\":\"not_available\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_018acGYLtfR52q9yDbWaEdQZ\",\"name\":\"get_weather\",\"input\":{},\"caller\":{\"type\":\"direct\"}} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"loca\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"tio\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"n\\\": \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\\\"San Fr\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"anci\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"sco, CA\\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\", \\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"units\\\": \\\"f\\\"}\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":656,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":74} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n" } }, { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages?beta=true", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "NOT_GIVEN", "accept": "application/json", "content-type": "application/json", "user-agent": "Anthropic/Python 0.82.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.82.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.9.18", "x-stainless-async": "false", "anthropic-version": "2023-06-01", "x-stainless-helper-method": "stream", "x-stainless-stream-helper": "beta.messages", "x-stainless-helper": "BetaToolRunner", "x-stainless-retry-count": "0", "x-stainless-read-timeout": "600", "content-length": "1001" }, "body": { "max_tokens": 1024, "messages": [ { "role": "user", "content": "What is the weather in SF?" }, { "role": "assistant", "content": [ { "id": "toolu_018acGYLtfR52q9yDbWaEdQZ", "input": { "location": "San Francisco, CA", "units": "f" }, "name": "get_weather", "type": "tool_use", "caller": { "type": "direct" } } ] }, { "role": "user", "content": [ { "type": "tool_result", "tool_use_id": "toolu_018acGYLtfR52q9yDbWaEdQZ", "content": "{\"location\": \"San Francisco, CA\", \"temperature\": \"68\\u00b0F\", \"condition\": \"Sunny\"}" } ] } ], "model": "claude-haiku-4-5", "tools": [ { "name": "get_weather", "description": "Lookup the weather for a given city in either celsius or fahrenheit", "input_schema": { "additionalProperties": false, "properties": { "location": { "description": "The city and state, e.g. San Francisco, CA", "title": "Location", "type": "string" }, "units": { "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit", "enum": [ "c", "f" ], "title": "Units", "type": "string" } }, "required": [ "location", "units" ], "type": "object" } } ], "stream": true } }, "response": { "status_code": 200, "headers": { "content-type": "text/event-stream; charset=utf-8", "connection": "keep-alive", "cache-control": "no-cache", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "x-robots-tag": "none", "content-security-policy": "default-src 'none'; frame-ancestors 'none'", "cf-cache-status": "DYNAMIC" }, "body": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-haiku-4-5-20251001\",\"id\":\"msg_016HxyUMAncysqX7dn1kWNRx\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":770,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":8,\"service_tier\":\"standard\",\"inference_geo\":\"not_available\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"The weather in San Francisco, CA is\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" currently\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\":\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n- **Temperature:**\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" 68°F\\n- **\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Condition:** Sunny\\n\\nIt\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"'s\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" a nice\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" sunny day!\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":770,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":38} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n" } } ]771c64ff-a0af-4cd9-8080-a5a539da7cb9.json000066400000000000000000000252071523216435200406550ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools[ { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages?beta=true", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "NOT_GIVEN", "accept": "application/json", "content-type": "application/json", "user-agent": "Anthropic/Python 0.82.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.82.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.9.18", "x-stainless-async": "false", "anthropic-version": "2023-06-01", "x-stainless-helper-method": "stream", "x-stainless-stream-helper": "beta.messages", "x-stainless-helper": "BetaToolRunner", "x-stainless-retry-count": "0", "x-stainless-read-timeout": "600", "content-length": "602" }, "body": { "max_tokens": 1024, "messages": [ { "role": "user", "content": "What is the weather in SF?" } ], "model": "claude-haiku-4-5", "tools": [ { "name": "get_weather", "description": "Lookup the weather for a given city in either celsius or fahrenheit", "input_schema": { "additionalProperties": false, "properties": { "location": { "description": "The city and state, e.g. San Francisco, CA", "title": "Location", "type": "string" }, "units": { "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit", "enum": [ "c", "f" ], "title": "Units", "type": "string" } }, "required": [ "location", "units" ], "type": "object" } } ], "stream": true } }, "response": { "status_code": 200, "headers": { "content-type": "text/event-stream; charset=utf-8", "connection": "keep-alive", "cache-control": "no-cache", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "x-robots-tag": "none", "content-security-policy": "default-src 'none'; frame-ancestors 'none'", "cf-cache-status": "DYNAMIC" }, "body": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-haiku-4-5-20251001\",\"id\":\"msg_01R4hRKPvDP3eyHsaAgs1gBn\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":656,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":26,\"service_tier\":\"standard\",\"inference_geo\":\"not_available\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"tool_use\",\"id\":\"toolu_01TJoxvFknVdnV9XpWFPaRmY\",\"name\":\"get_weather\",\"input\":{},\"caller\":{\"type\":\"direct\"}} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"location\\\":\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\" \\\"San\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\" Francisco, \"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"CA\\\"\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\", \\\"units\\\": \"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"\\\"f\\\"}\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"tool_use\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":656,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":74} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n" } }, { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages?beta=true", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "NOT_GIVEN", "accept": "application/json", "content-type": "application/json", "user-agent": "Anthropic/Python 0.82.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.82.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.9.18", "x-stainless-async": "false", "anthropic-version": "2023-06-01", "x-stainless-helper-method": "stream", "x-stainless-stream-helper": "beta.messages", "x-stainless-helper": "BetaToolRunner", "x-stainless-retry-count": "0", "x-stainless-read-timeout": "600", "content-length": "1001" }, "body": { "max_tokens": 1024, "messages": [ { "role": "user", "content": "What is the weather in SF?" }, { "role": "assistant", "content": [ { "id": "toolu_01TJoxvFknVdnV9XpWFPaRmY", "input": { "location": "San Francisco, CA", "units": "f" }, "name": "get_weather", "type": "tool_use", "caller": { "type": "direct" } } ] }, { "role": "user", "content": [ { "type": "tool_result", "tool_use_id": "toolu_01TJoxvFknVdnV9XpWFPaRmY", "content": "{\"location\": \"San Francisco, CA\", \"temperature\": \"68\\u00b0F\", \"condition\": \"Sunny\"}" } ] } ], "model": "claude-haiku-4-5", "tools": [ { "name": "get_weather", "description": "Lookup the weather for a given city in either celsius or fahrenheit", "input_schema": { "additionalProperties": false, "properties": { "location": { "description": "The city and state, e.g. San Francisco, CA", "title": "Location", "type": "string" }, "units": { "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit", "enum": [ "c", "f" ], "title": "Units", "type": "string" } }, "required": [ "location", "units" ], "type": "object" } } ], "stream": true } }, "response": { "status_code": 200, "headers": { "content-type": "text/event-stream; charset=utf-8", "connection": "keep-alive", "cache-control": "no-cache", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "x-robots-tag": "none", "content-security-policy": "default-src 'none'; frame-ancestors 'none'", "cf-cache-status": "DYNAMIC" }, "body": "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-haiku-4-5-20251001\",\"id\":\"msg_0158JyopQTFaomteeJoDpS5q\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":770,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":8,\"service_tier\":\"standard\",\"inference_geo\":\"not_available\"}} }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"The weather in San Francisco, CA is\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" currently\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" **\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"68°F an\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d Sunny**. It's\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" a nice\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" day!\"} }\n\nevent: content_block_stop\ndata: {\"type\":\"content_block_stop\",\"index\":0}\n\nevent: message_delta\ndata: {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":770,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":27} }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n" } } ]956fa2fe-8752-4f7c-8f9a-33735e62b898.json000066400000000000000000000340721523216435200404160ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools[ { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages?beta=true", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "600", "accept": "application/json", "content-type": "application/json", "user-agent": "Anthropic/Python 0.82.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.82.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.9.18", "x-stainless-async": "false", "anthropic-version": "2023-06-01", "x-stainless-helper": "BetaToolRunner", "anthropic-beta": "structured-outputs-2025-12-15", "x-stainless-retry-count": "0", "x-stainless-read-timeout": "600", "content-length": "530" }, "body": { "max_tokens": 4000, "messages": [ { "role": "user", "content": "Write a detailed 500 word essay about dogs, cats, and birds. Call the tool submit_analysis with the information about all three animals. Note that you should call it only once at the end of your essay." } ], "model": "claude-sonnet-4-5", "tools": [ { "name": "submit_analysis", "description": "Call this LAST with your final analysis.", "input_schema": { "additionalProperties": false, "properties": { "summary": { "title": "Summary", "type": "string" } }, "required": [ "summary" ], "type": "object" } } ] } }, "response": { "status_code": 200, "headers": { "content-type": "application/json", "connection": "keep-alive", "x-robots-tag": "none", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "content-security-policy": "default-src 'none'; frame-ancestors 'none'", "cf-cache-status": "DYNAMIC" }, "body": { "model": "claude-sonnet-4-5-20250929", "id": "msg_01JQ78eECNHtwV1L2uGKLHAf", "type": "message", "role": "assistant", "content": [ { "type": "text", "text": "# The Wonderful World of Companion Animals: Dogs, Cats, and Birds\n\nThroughout human history, animals have played an integral role in our lives, providing companionship, entertainment, and emotional support. Among the most popular pets worldwide are dogs, cats, and birds, each offering unique characteristics and benefits that have endeared them to millions of households globally.\n\n## Dogs: Mankind's Best Friend\n\nDogs have rightfully earned their title as \"man's best friend\" through thousands of years of domestication and selective breeding. These loyal canines come in an astounding variety of breeds, from tiny Chihuahuas to massive Great Danes, each with distinct personalities and purposes. What sets dogs apart is their unwavering devotion and their ability to form deep emotional bonds with their human families. They are highly social animals that thrive on interaction and attention, making them ideal companions for active individuals and families.\n\nDogs require significant commitment, including regular exercise, training, and socialization. Their intelligence allows them to learn commands, perform tasks, and even assist people with disabilities as service animals. Whether it's a morning jog, a game of fetch, or simply relaxing on the couch, dogs eagerly participate in their owners' daily activities. Their protective instincts also make them excellent guardians of the home, alerting families to potential dangers while providing a sense of security.\n\n## Cats: Independent Companions\n\nCats offer a different kind of companionship, appealing to those who appreciate a more independent pet. These graceful felines are known for their self-sufficient nature, requiring less hands-on attention than dogs while still providing affection and entertainment. Cats are natural hunters with remarkable agility and reflexes, often displaying their instincts through play and exploration.\n\nDespite their reputation for aloofness, cats form strong bonds with their owners, showing affection through purring, head-butting, and kneading. They are relatively low-maintenance pets, naturally inclined to groom themselves and use litter boxes with minimal training. Cats are perfect for apartment living and busy lifestyles, as they don't require outdoor exercise and are content spending time alone during work hours. Their playful antics and curious nature provide endless entertainment, while their calming purrs have been shown to reduce stress and anxiety in their human companions.\n\n## Birds: Colorful and Intelligent\n\nBirds bring vibrant colors, beautiful songs, and surprising intelligence to the world of pet ownership. From small budgies and canaries to larger parrots and cockatoos, birds offer diverse options for different living situations and experience levels. Many bird species are remarkably intelligent, capable of learning tricks, mimicking speech, and solving puzzles.\n\nSocial by nature, birds thrive on interaction and mental stimulation. They require specialized care, including proper diet, spacious cages, and regular out-of-cage time for exercise and bonding. Their melodious songs and calls can brighten any home, though potential owners should consider noise levels, especially with larger parrot species. Birds can live for many years, with some parrots surviving for decades, making them long-term companions that become integral family members.\n\n## Conclusion\n\nWhether you prefer the loyal enthusiasm of dogs, the independent grace of cats, or the colorful intelligence of birds, each of these animals offers unique rewards to those who welcome them into their homes. The choice ultimately depends on lifestyle, living situation, and personal preferences, but all three have proven themselves as beloved companions throughout human history." }, { "type": "tool_use", "id": "toolu_01KiHQYXfTgCmpgRfmqgvUL2", "name": "submit_analysis", "input": { "summary": "This comprehensive essay explores three popular companion animals: dogs, cats, and birds. Dogs are highlighted as loyal, social animals requiring significant commitment but offering unwavering devotion, protection, and the ability to participate actively in human activities. They come in various breeds and can serve as service animals due to their intelligence. Cats are presented as independent, low-maintenance companions perfect for apartment living and busy lifestyles. They are self-sufficient, naturally clean, and provide affection while reducing stress through their calming presence. Birds are described as colorful, intelligent pets ranging from small budgies to large parrots, capable of mimicking speech and learning tricks. They require specialized care, thrive on social interaction, and can live for many years, becoming long-term family members. The essay concludes that each animal offers unique benefits, and the choice depends on individual lifestyle, living situation, and personal preferences, with all three having proven themselves as beloved companions throughout human history." }, "caller": { "type": "direct" } } ], "stop_reason": "tool_use", "stop_sequence": null, "usage": { "input_tokens": 617, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": { "ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0 }, "output_tokens": 995, "service_tier": "standard", "inference_geo": "not_available" } } } }, { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages?beta=true", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "600", "accept": "application/json", "content-type": "application/json", "user-agent": "Anthropic/Python 0.82.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.82.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.9.18", "x-stainless-async": "false", "anthropic-version": "2023-06-01", "x-stainless-helper": "compaction", "x-stainless-retry-count": "0", "x-stainless-read-timeout": "600", "content-length": "1666" }, "body": { "max_tokens": 4000, "messages": [ { "role": "user", "content": "Write a detailed 500 word essay about dogs, cats, and birds. Call the tool submit_analysis with the information about all three animals. Note that you should call it only once at the end of your essay." }, { "role": "user", "content": "You have been working on the task described above but have not yet completed it. Write a continuation summary that will allow you (or another instance of yourself) to resume work efficiently in a future context window where the conversation history will be replaced with this summary. Your summary should be structured, concise, and actionable. Include:\n1. Task Overview\nThe user's core request and success criteria\nAny clarifications or constraints they specified\n2. Current State\nWhat has been completed so far\nFiles created, modified, or analyzed (with paths if relevant)\nKey outputs or artifacts produced\n3. Important Discoveries\nTechnical constraints or requirements uncovered\nDecisions made and their rationale\nErrors encountered and how they were resolved\nWhat approaches were tried that didn't work (and why)\n4. Next Steps\nSpecific actions needed to complete the task\nAny blockers or open questions to resolve\nPriority order if multiple steps remain\n5. Context to Preserve\nUser preferences or style requirements\nDomain-specific details that aren't obvious\nAny promises made to the user\nBe concise but complete—err on the side of including information that would prevent duplicate work or repeated mistakes. Write in a way that enables immediate resumption of the task.\nWrap your summary in tags." } ], "model": "claude-sonnet-4-5" } }, "response": { "status_code": 200, "headers": { "content-type": "application/json", "connection": "keep-alive", "x-robots-tag": "none", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "content-security-policy": "default-src 'none'; frame-ancestors 'none'", "cf-cache-status": "DYNAMIC" }, "body": { "model": "claude-sonnet-4-5-20250929", "id": "msg_019bVBTjMoXMbUV6LJyiDAmM", "type": "message", "role": "assistant", "content": [ { "type": "text", "text": "\n## Task Overview\nThe user requests a detailed 500-word essay about dogs, cats, and birds, followed by a single call to the `submit_analysis` tool at the end containing information about all three animals. \n\n**Key Requirements:**\n- Essay must be 500 words in length\n- Cover dogs, cats, and birds\n- Call `submit_analysis` tool only once at the completion\n- The tool call should contain information about all three animals\n\n## Current State\n**Status:** Not started - no work has been completed yet.\n\n**Completed:**\n- None\n\n**Artifacts Produced:**\n- None\n\n## Important Discoveries\n**Unknown Information:**\n- The exact structure/parameters expected by the `submit_analysis` tool (need to determine what format the tool accepts)\n- Whether the tool requires specific data fields for each animal or free-form text\n- The level of detail expected in the analysis (scientific, casual, comparative, etc.)\n\n**Assumptions to Verify:**\n- The essay should likely compare/contrast the three animals as pets or discuss their characteristics\n- The `submit_analysis` tool probably accepts structured data about the animals\n\n## Next Steps\n1. **Write the 500-word essay** covering:\n - Dogs (characteristics, behavior, role as pets)\n - Cats (characteristics, behavior, role as pets)\n - Birds (characteristics, behavior, role as pets)\n - Potentially comparative elements between the three\n\n2. **Determine the `submit_analysis` tool structure** - check what parameters it accepts\n\n3. **Call `submit_analysis` once** with comprehensive information about all three animals in the appropriate format\n\n4. **Verify word count** is approximately 500 words before submitting\n\n## Context to Preserve\n- User emphasized calling the tool \"only once at the end\" - this is a specific constraint to respect\n- The tool should contain information about \"all three animals\" - comprehensive coverage required\n- Essay should be \"detailed\" - suggests substantive content rather than superficial treatment\n\n## Priority\nHigh priority on understanding the `submit_analysis` tool parameters before writing the essay, as the content may need to be structured to align with tool requirements.\n" } ], "stop_reason": "end_turn", "stop_sequence": null, "usage": { "input_tokens": 324, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": { "ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0 }, "output_tokens": 486, "service_tier": "standard", "inference_geo": "not_available" } } } } ]a8ac789b-f856-48cd-9ff3-d5f36799e432.json000066400000000000000000000161741523216435200405640ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools[ { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages?beta=true", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "600", "accept": "application/json", "content-type": "application/json", "user-agent": "Anthropic/Python 0.82.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.82.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.9.18", "x-stainless-async": "false", "anthropic-version": "2023-06-01", "x-stainless-helper": "BetaToolRunner", "anthropic-beta": "structured-outputs-2025-12-15", "x-stainless-retry-count": "0", "x-stainless-read-timeout": "600", "content-length": "390" }, "body": { "max_tokens": 1024, "messages": [ { "role": "user", "content": "What's the weather in SF in Celsius?" } ], "model": "claude-haiku-4-5", "tools": [ { "name": "get_weather", "description": "", "input_schema": { "additionalProperties": false, "properties": { "location": { "title": "Location", "type": "string" }, "units": { "enum": [ "c", "f" ], "title": "Units", "type": "string" } }, "required": [ "location", "units" ], "type": "object" } } ] } }, "response": { "status_code": 200, "headers": { "content-type": "application/json", "connection": "keep-alive", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "cf-cache-status": "DYNAMIC", "x-robots-tag": "none", "content-security-policy": "default-src 'none'; frame-ancestors 'none'" }, "body": { "model": "claude-haiku-4-5-20251001", "id": "msg_01M4x4hiFuUdHzu44ih9eCGh", "type": "message", "role": "assistant", "content": [ { "type": "tool_use", "id": "toolu_013DU6hV4C1M8dJ32ybQFAFi", "name": "get_weather", "input": { "location": "SF", "units": "c" }, "caller": { "type": "direct" } } ], "stop_reason": "tool_use", "stop_sequence": null, "usage": { "input_tokens": 597, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": { "ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0 }, "output_tokens": 71, "service_tier": "standard", "inference_geo": "not_available" } } } }, { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages?beta=true", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "600", "accept": "application/json", "content-type": "application/json", "user-agent": "Anthropic/Python 0.82.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.82.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.9.18", "x-stainless-async": "false", "anthropic-version": "2023-06-01", "x-stainless-helper": "BetaToolRunner", "anthropic-beta": "structured-outputs-2025-12-15", "x-stainless-retry-count": "0", "x-stainless-read-timeout": "600", "content-length": "759" }, "body": { "max_tokens": 1024, "messages": [ { "role": "user", "content": "What's the weather in SF in Celsius?" }, { "role": "assistant", "content": [ { "id": "toolu_013DU6hV4C1M8dJ32ybQFAFi", "input": { "location": "SF", "units": "c" }, "name": "get_weather", "type": "tool_use", "caller": { "type": "direct" } } ] }, { "role": "user", "content": [ { "type": "tool_result", "tool_use_id": "toolu_013DU6hV4C1M8dJ32ybQFAFi", "content": "{\"location\": \"SF\", \"temperature\": \"20\\u00b0C\", \"condition\": \"Sunny\"}" } ] } ], "model": "claude-haiku-4-5", "tools": [ { "name": "get_weather", "description": "", "input_schema": { "additionalProperties": false, "properties": { "location": { "title": "Location", "type": "string" }, "units": { "enum": [ "c", "f" ], "title": "Units", "type": "string" } }, "required": [ "location", "units" ], "type": "object" } } ] } }, "response": { "status_code": 200, "headers": { "content-type": "application/json", "connection": "keep-alive", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "cf-cache-status": "DYNAMIC", "x-robots-tag": "none", "content-security-policy": "default-src 'none'; frame-ancestors 'none'" }, "body": { "model": "claude-haiku-4-5-20251001", "id": "msg_01LzoWDaDa7jiMvVbBiguxJy", "type": "message", "role": "assistant", "content": [ { "type": "text", "text": "The weather in SF is currently **20°C** (68°F) and **Sunny**!" } ], "stop_reason": "end_turn", "stop_sequence": null, "usage": { "input_tokens": 705, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": { "ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0 }, "output_tokens": 25, "service_tier": "standard", "inference_geo": "not_available" } } } } ]b38bbf6c-9a76-40ca-b09d-7a3911776e0f.json000066400000000000000000000172361523216435200405750ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools[ { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages?beta=true", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "600", "accept": "application/json", "content-type": "application/json", "user-agent": "Anthropic/Python 0.82.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.82.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.9.18", "x-stainless-async": "false", "anthropic-version": "2023-06-01", "x-stainless-helper": "BetaToolRunner", "anthropic-beta": "structured-outputs-2025-12-15", "x-stainless-retry-count": "0", "x-stainless-read-timeout": "600", "content-length": "588" }, "body": { "max_tokens": 1024, "messages": [ { "role": "user", "content": "What is the weather in SF?" } ], "model": "claude-haiku-4-5", "tools": [ { "name": "get_weather", "description": "Lookup the weather for a given city in either celsius or fahrenheit", "input_schema": { "additionalProperties": false, "properties": { "location": { "description": "The city and state, e.g. San Francisco, CA", "title": "Location", "type": "string" }, "units": { "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit", "enum": [ "c", "f" ], "title": "Units", "type": "string" } }, "required": [ "location", "units" ], "type": "object" } } ] } }, "response": { "status_code": 200, "headers": { "content-type": "application/json", "connection": "keep-alive", "x-robots-tag": "none", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "content-security-policy": "default-src 'none'; frame-ancestors 'none'", "cf-cache-status": "DYNAMIC" }, "body": { "model": "claude-haiku-4-5-20251001", "id": "msg_018yE33RyaCdsMnr8kGYUQ5Y", "type": "message", "role": "assistant", "content": [ { "type": "tool_use", "id": "toolu_011bpynHqFZ9P4u5rSaXsTJQ", "name": "get_weather", "input": { "location": "San Francisco, CA", "units": "f" }, "caller": { "type": "direct" } } ], "stop_reason": "tool_use", "stop_sequence": null, "usage": { "input_tokens": 656, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": { "ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0 }, "output_tokens": 74, "service_tier": "standard", "inference_geo": "not_available" } } } }, { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages?beta=true", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "600", "accept": "application/json", "content-type": "application/json", "user-agent": "Anthropic/Python 0.82.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.82.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.9.18", "x-stainless-async": "false", "anthropic-version": "2023-06-01", "x-stainless-helper": "BetaToolRunner", "anthropic-beta": "structured-outputs-2025-12-15", "x-stainless-retry-count": "0", "x-stainless-read-timeout": "600", "content-length": "987" }, "body": { "max_tokens": 1024, "messages": [ { "role": "user", "content": "What is the weather in SF?" }, { "role": "assistant", "content": [ { "id": "toolu_011bpynHqFZ9P4u5rSaXsTJQ", "input": { "location": "San Francisco, CA", "units": "f" }, "name": "get_weather", "type": "tool_use", "caller": { "type": "direct" } } ] }, { "role": "user", "content": [ { "type": "tool_result", "tool_use_id": "toolu_011bpynHqFZ9P4u5rSaXsTJQ", "content": "{\"location\": \"San Francisco, CA\", \"temperature\": \"68\\u00b0F\", \"condition\": \"Sunny\"}" } ] } ], "model": "claude-haiku-4-5", "tools": [ { "name": "get_weather", "description": "Lookup the weather for a given city in either celsius or fahrenheit", "input_schema": { "additionalProperties": false, "properties": { "location": { "description": "The city and state, e.g. San Francisco, CA", "title": "Location", "type": "string" }, "units": { "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit", "enum": [ "c", "f" ], "title": "Units", "type": "string" } }, "required": [ "location", "units" ], "type": "object" } } ] } }, "response": { "status_code": 200, "headers": { "content-type": "application/json", "connection": "keep-alive", "x-robots-tag": "none", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "content-security-policy": "default-src 'none'; frame-ancestors 'none'", "cf-cache-status": "DYNAMIC" }, "body": { "model": "claude-haiku-4-5-20251001", "id": "msg_01BZsMQjer9AFLgmdRKJ8NcA", "type": "message", "role": "assistant", "content": [ { "type": "text", "text": "The weather in San Francisco, CA is currently **Sunny** with a temperature of **68°F**." } ], "stop_reason": "end_turn", "stop_sequence": null, "usage": { "input_tokens": 770, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": { "ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0 }, "output_tokens": 25, "service_tier": "standard", "inference_geo": "not_available" } } } } ]e075a6c2-de4d-4125-9709-f0e178058190.json000066400000000000000000000211631523216435200402110ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools[ { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages?beta=true", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "600", "accept": "application/json", "content-type": "application/json", "user-agent": "Anthropic/Python 0.82.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.82.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.9.18", "x-stainless-async": "false", "anthropic-version": "2023-06-01", "x-stainless-helper": "BetaToolRunner", "anthropic-beta": "structured-outputs-2025-12-15", "x-stainless-retry-count": "0", "x-stainless-read-timeout": "600", "content-length": "779" }, "body": { "max_tokens": 1024, "messages": [ { "role": "user", "content": "What's the weather in San Francisco, New York, London, Tokyo and Paris?If you need to use tools, call only one tool at a time. Wait for the tool'sresponse before making another call. Never call multiple tools at once." } ], "model": "claude-haiku-4-5", "tools": [ { "name": "get_weather", "description": "Lookup the weather for a given city in either celsius or fahrenheit", "input_schema": { "additionalProperties": false, "properties": { "location": { "description": "The city and state, e.g. San Francisco, CA", "title": "Location", "type": "string" }, "units": { "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit", "enum": [ "c", "f" ], "title": "Units", "type": "string" } }, "required": [ "location", "units" ], "type": "object" } } ] } }, "response": { "status_code": 200, "headers": { "content-type": "application/json", "connection": "keep-alive", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "cf-cache-status": "DYNAMIC", "x-robots-tag": "none", "content-security-policy": "default-src 'none'; frame-ancestors 'none'" }, "body": { "model": "claude-haiku-4-5-20251001", "id": "msg_01UBZt9MX63Tk3v1gKvgxk3A", "type": "message", "role": "assistant", "content": [ { "type": "text", "text": "I'll get the weather for each of those cities. Let me start by checking San Francisco." }, { "type": "tool_use", "id": "toolu_01LRanfq6DmHn1yDTB4d1SAh", "name": "get_weather", "input": { "location": "San Francisco, CA", "units": "f" }, "caller": { "type": "direct" } } ], "stop_reason": "tool_use", "stop_sequence": null, "usage": { "input_tokens": 701, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": { "ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0 }, "output_tokens": 93, "service_tier": "standard", "inference_geo": "not_available" } } } }, { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages?beta=true", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "600", "accept": "application/json", "content-type": "application/json", "user-agent": "Anthropic/Python 0.82.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.82.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.9.18", "x-stainless-async": "false", "anthropic-version": "2023-06-01", "x-stainless-helper": "BetaToolRunner", "anthropic-beta": "structured-outputs-2025-12-15", "x-stainless-retry-count": "0", "x-stainless-read-timeout": "600", "content-length": "1290" }, "body": { "max_tokens": 1024, "messages": [ { "role": "user", "content": "What's the weather in San Francisco, New York, London, Tokyo and Paris?If you need to use tools, call only one tool at a time. Wait for the tool'sresponse before making another call. Never call multiple tools at once." }, { "role": "assistant", "content": [ { "text": "I'll get the weather for each of those cities. Let me start by checking San Francisco.", "type": "text" }, { "id": "toolu_01LRanfq6DmHn1yDTB4d1SAh", "input": { "location": "San Francisco, CA", "units": "f" }, "name": "get_weather", "type": "tool_use", "caller": { "type": "direct" } } ] }, { "role": "user", "content": [ { "type": "tool_result", "tool_use_id": "toolu_01LRanfq6DmHn1yDTB4d1SAh", "content": "{\"location\": \"San Francisco, CA\", \"temperature\": \"68\\u00b0F\", \"condition\": \"Sunny\"}" } ] } ], "model": "claude-haiku-4-5", "tools": [ { "name": "get_weather", "description": "Lookup the weather for a given city in either celsius or fahrenheit", "input_schema": { "additionalProperties": false, "properties": { "location": { "description": "The city and state, e.g. San Francisco, CA", "title": "Location", "type": "string" }, "units": { "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit", "enum": [ "c", "f" ], "title": "Units", "type": "string" } }, "required": [ "location", "units" ], "type": "object" } } ] } }, "response": { "status_code": 200, "headers": { "content-type": "application/json", "connection": "keep-alive", "x-robots-tag": "none", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "content-security-policy": "default-src 'none'; frame-ancestors 'none'", "cf-cache-status": "DYNAMIC" }, "body": { "model": "claude-haiku-4-5-20251001", "id": "msg_01BAceCxj9VxXR9GhBedwTm2", "type": "message", "role": "assistant", "content": [ { "type": "text", "text": "Now let me check New York." }, { "type": "tool_use", "id": "toolu_01RWdcDdE8NAFDgZ8F9Xk2K7", "name": "get_weather", "input": { "location": "New York, NY", "units": "f" }, "caller": { "type": "direct" } } ], "stop_reason": "tool_use", "stop_sequence": null, "usage": { "input_tokens": 834, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": { "ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0 }, "output_tokens": 81, "service_tier": "standard", "inference_geo": "not_available" } } } } ]f59a9391-643b-422c-96dc-1f28bc7ea4d7.json000066400000000000000000000155571523216435200405330ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/tools/__inline_snapshot__/test_runners/TestSyncRunTools[ { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages?beta=true", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "600", "accept": "application/json", "content-type": "application/json", "user-agent": "Anthropic/Python 0.82.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.82.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.9.18", "x-stainless-async": "false", "anthropic-version": "2023-06-01", "x-stainless-helper": "BetaToolRunner", "anthropic-beta": "structured-outputs-2025-12-15", "x-stainless-retry-count": "0", "x-stainless-read-timeout": "600", "content-length": "598" }, "body": { "max_tokens": 1024, "messages": [ { "role": "user", "content": "What's the weather in SF in Celsius?" } ], "model": "claude-haiku-4-5", "tools": [ { "name": "get_weather", "description": "Lookup the weather for a given city in either celsius or fahrenheit", "input_schema": { "additionalProperties": false, "properties": { "location": { "description": "The city and state, e.g. San Francisco, CA", "title": "Location", "type": "string" }, "units": { "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit", "enum": [ "c", "f" ], "title": "Units", "type": "string" } }, "required": [ "location", "units" ], "type": "object" } } ] } }, "response": { "status_code": 200, "headers": { "content-type": "application/json", "connection": "keep-alive", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "cf-cache-status": "DYNAMIC", "x-robots-tag": "none", "content-security-policy": "default-src 'none'; frame-ancestors 'none'" }, "body": { "model": "claude-haiku-4-5-20251001", "id": "msg_01QCEBW49MBPHFuFwMDoFtPP", "type": "message", "role": "assistant", "content": [ { "type": "tool_use", "id": "toolu_01GHndag5wQmbzNihYmV2UBj", "name": "get_weather", "input": { "location": "San Francisco, CA", "units": "c" }, "caller": { "type": "direct" } } ], "stop_reason": "tool_use", "stop_sequence": null, "usage": { "input_tokens": 659, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": { "ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0 }, "output_tokens": 74, "service_tier": "standard", "inference_geo": "not_available" } } } }, { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages?beta=true", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "600", "accept": "application/json", "content-type": "application/json", "user-agent": "Anthropic/Python 0.82.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.82.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.9.18", "x-stainless-async": "false", "anthropic-version": "2023-06-01", "x-stainless-helper": "BetaToolRunner", "anthropic-beta": "structured-outputs-2025-12-15", "x-stainless-retry-count": "0", "x-stainless-read-timeout": "600", "content-length": "789" }, "body": { "max_tokens": 1024, "messages": [ { "role": "user", "content": "What's the weather in SF in Celsius?" }, { "role": "user", "content": [ { "tool_use_id": "toolu_01GHndag5wQmbzNihYmV2UBj", "content": "The weather in San Francisco, CA is currently sunny with a temperature of 20°C.", "type": "tool_result" } ] } ], "model": "claude-haiku-4-5", "tools": [ { "name": "get_weather", "description": "Lookup the weather for a given city in either celsius or fahrenheit", "input_schema": { "additionalProperties": false, "properties": { "location": { "description": "The city and state, e.g. San Francisco, CA", "title": "Location", "type": "string" }, "units": { "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit", "enum": [ "c", "f" ], "title": "Units", "type": "string" } }, "required": [ "location", "units" ], "type": "object" } } ] } }, "response": { "status_code": 400, "headers": { "content-type": "application/json", "content-length": "316", "connection": "keep-alive", "x-should-retry": "false", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "cf-cache-status": "DYNAMIC", "x-robots-tag": "none", "content-security-policy": "default-src 'none'; frame-ancestors 'none'" }, "body": { "type": "error", "error": { "type": "invalid_request_error", "message": "messages.0.content.1: unexpected `tool_use_id` found in `tool_result` blocks: toolu_01GHndag5wQmbzNihYmV2UBj. Each `tool_result` block must have a corresponding `tool_use` block in the previous message." }, "request_id": "req_011CYHyk9NPsBYeGbC9LuDNK" } } } ]anthropic-sdk-python-0.120.2/tests/lib/tools/__inline_snapshot__/test_runners/__module__/000077500000000000000000000000001523216435200316215ustar00rootroot0000000000000064fe7974-681a-4023-9848-b32ba39c8664.json000066400000000000000000000172671523216435200367640ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/tools/__inline_snapshot__/test_runners/__module__[ { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages?beta=true", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "600", "accept": "application/json", "content-type": "application/json", "user-agent": "AsyncAnthropic/Python 0.82.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.82.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.9.18", "x-stainless-async": "async:asyncio", "anthropic-version": "2023-06-01", "x-stainless-helper": "BetaToolRunner", "anthropic-beta": "structured-outputs-2025-12-15", "x-stainless-retry-count": "0", "x-stainless-read-timeout": "600", "content-length": "588" }, "body": { "max_tokens": 1024, "messages": [ { "role": "user", "content": "What is the weather in SF?" } ], "model": "claude-haiku-4-5", "tools": [ { "name": "get_weather", "description": "Lookup the weather for a given city in either celsius or fahrenheit", "input_schema": { "additionalProperties": false, "properties": { "location": { "description": "The city and state, e.g. San Francisco, CA", "title": "Location", "type": "string" }, "units": { "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit", "enum": [ "c", "f" ], "title": "Units", "type": "string" } }, "required": [ "location", "units" ], "type": "object" } } ] } }, "response": { "status_code": 200, "headers": { "content-type": "application/json", "connection": "keep-alive", "x-robots-tag": "none", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "content-security-policy": "default-src 'none'; frame-ancestors 'none'", "cf-cache-status": "DYNAMIC" }, "body": { "model": "claude-haiku-4-5-20251001", "id": "msg_01YPmXusvsWu64vxcNZf1uJq", "type": "message", "role": "assistant", "content": [ { "type": "tool_use", "id": "toolu_016xm9m1i3NcGW5xFMMZJTqY", "name": "get_weather", "input": { "location": "San Francisco, CA", "units": "f" }, "caller": { "type": "direct" } } ], "stop_reason": "tool_use", "stop_sequence": null, "usage": { "input_tokens": 656, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": { "ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0 }, "output_tokens": 74, "service_tier": "standard", "inference_geo": "not_available" } } } }, { "request": { "method": "POST", "url": "https://api.anthropic.com/v1/messages?beta=true", "headers": { "host": "api.anthropic.com", "accept-encoding": "gzip, deflate", "connection": "keep-alive", "x-stainless-timeout": "600", "accept": "application/json", "content-type": "application/json", "user-agent": "AsyncAnthropic/Python 0.82.0", "x-stainless-lang": "python", "x-stainless-package-version": "0.82.0", "x-stainless-os": "MacOS", "x-stainless-arch": "arm64", "x-stainless-runtime": "CPython", "x-stainless-runtime-version": "3.9.18", "x-stainless-async": "async:asyncio", "anthropic-version": "2023-06-01", "x-stainless-helper": "BetaToolRunner", "anthropic-beta": "structured-outputs-2025-12-15", "x-stainless-retry-count": "0", "x-stainless-read-timeout": "600", "content-length": "987" }, "body": { "max_tokens": 1024, "messages": [ { "role": "user", "content": "What is the weather in SF?" }, { "role": "assistant", "content": [ { "id": "toolu_016xm9m1i3NcGW5xFMMZJTqY", "input": { "location": "San Francisco, CA", "units": "f" }, "name": "get_weather", "type": "tool_use", "caller": { "type": "direct" } } ] }, { "role": "user", "content": [ { "type": "tool_result", "tool_use_id": "toolu_016xm9m1i3NcGW5xFMMZJTqY", "content": "{\"location\": \"San Francisco, CA\", \"temperature\": \"68\\u00b0F\", \"condition\": \"Sunny\"}" } ] } ], "model": "claude-haiku-4-5", "tools": [ { "name": "get_weather", "description": "Lookup the weather for a given city in either celsius or fahrenheit", "input_schema": { "additionalProperties": false, "properties": { "location": { "description": "The city and state, e.g. San Francisco, CA", "title": "Location", "type": "string" }, "units": { "description": "Unit for the output, either 'c' for celsius or 'f' for fahrenheit", "enum": [ "c", "f" ], "title": "Units", "type": "string" } }, "required": [ "location", "units" ], "type": "object" } } ] } }, "response": { "status_code": 200, "headers": { "content-type": "application/json", "connection": "keep-alive", "x-robots-tag": "none", "strict-transport-security": "max-age=31536000; includeSubDomains; preload", "server": "cloudflare", "content-security-policy": "default-src 'none'; frame-ancestors 'none'", "cf-cache-status": "DYNAMIC" }, "body": { "model": "claude-haiku-4-5-20251001", "id": "msg_01C1RRE9d8CxcudwbihWU9di", "type": "message", "role": "assistant", "content": [ { "type": "text", "text": "The weather in San Francisco, CA is currently **68°F and Sunny**. Great day out there!" } ], "stop_reason": "end_turn", "stop_sequence": null, "usage": { "input_tokens": 770, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": { "ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0 }, "output_tokens": 26, "service_tier": "standard", "inference_geo": "not_available" } } } } ]anthropic-sdk-python-0.120.2/tests/lib/tools/memory_tools/000077500000000000000000000000001523216435200235445ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/lib/tools/memory_tools/test_filesystem.py000066400000000000000000001347211523216435200273510ustar00rootroot00000000000000from __future__ import annotations import os import stat import tempfile from typing import Iterator from pathlib import Path import pytest from anthropic.types.beta import ( BetaMemoryTool20250818ViewCommand, BetaMemoryTool20250818CreateCommand, BetaMemoryTool20250818DeleteCommand, BetaMemoryTool20250818InsertCommand, BetaMemoryTool20250818RenameCommand, BetaMemoryTool20250818StrReplaceCommand, ) from anthropic.lib.tools._beta_functions import ToolError from anthropic.lib.tools._beta_builtin_memory_tool import ( BetaLocalFilesystemMemoryTool, BetaAsyncLocalFilesystemMemoryTool, ) @pytest.fixture def temp_directory() -> Iterator[str]: with tempfile.TemporaryDirectory() as tmpdirname: yield tmpdirname @pytest.fixture def sync_local_filesystem_tool(temp_directory: str) -> BetaLocalFilesystemMemoryTool: return BetaLocalFilesystemMemoryTool(base_path=temp_directory) @pytest.fixture def async_local_filesystem_tool(temp_directory: str) -> BetaAsyncLocalFilesystemMemoryTool: return BetaAsyncLocalFilesystemMemoryTool(base_path=temp_directory) def get_directory_snapshot(base_path: str) -> dict[str, str]: """Get a snapshot of directory contents with relative paths.""" snapshot: dict[str, str] = {} for root, _, files in os.walk(base_path): for file in files: full_path = os.path.join(root, file) rel_path = os.path.relpath(full_path, base_path) with open(full_path, "r") as f: snapshot[rel_path] = f.read() return snapshot class TestBetaLocalFilesystemMemoryTool: def test_mkdir_parents_not_world_writable_under_permissive_umask(self) -> None: if os.name != "posix": pytest.skip("POSIX mode bits only") # Permissive umask: newly created dirs would default to 0o777 unless an # explicit mode is enforced on every component of the tree. old_umask = os.umask(0) try: with tempfile.TemporaryDirectory() as tmp: # base_path's parent ("nested") does NOT exist yet, so the tool must # create it as an intermediate parent. The bug: pathlib applies the # 0o700 mode only to the leaf, leaving these intermediate parents at # the umask default (0o777 here) — world-writable. base_path = Path(tmp) / "nested" / "memory" tool = BetaLocalFilesystemMemoryTool(base_path=str(base_path)) for directory in (base_path.parent, base_path, tool.memory_root): mode = stat.S_IMODE(directory.stat().st_mode) assert mode == 0o700, f"{directory} has mode {oct(mode)}, expected 0o700" assert not (mode & 0o077), f"{directory} is group/other-accessible: {oct(mode)}" finally: os.umask(old_umask) def test_create(self, sync_local_filesystem_tool: BetaLocalFilesystemMemoryTool) -> None: result = sync_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="Hello, World!", path="/memories/test_file.txt", ) ) assert result == "File created successfully at: /memories/test_file.txt" dir_snapshot = get_directory_snapshot(str(sync_local_filesystem_tool.base_path)) assert dir_snapshot == {"memories/test_file.txt": "Hello, World!"} def test_create_nested_directories(self, sync_local_filesystem_tool: BetaLocalFilesystemMemoryTool) -> None: result = sync_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="Nested file", path="/memories/deep/nested/file.txt", ) ) assert result == "File created successfully at: /memories/deep/nested/file.txt" dir_snapshot = get_directory_snapshot(str(sync_local_filesystem_tool.base_path)) assert dir_snapshot == {"memories/deep/nested/file.txt": "Nested file"} def test_create_error_if_file_already_exists( self, sync_local_filesystem_tool: BetaLocalFilesystemMemoryTool ) -> None: sync_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="Original", path="/memories/existing.txt", ) ) with pytest.raises(ToolError, match="File /memories/existing.txt already exists"): sync_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="New", path="/memories/existing.txt", ) ) def test_view_file(self, sync_local_filesystem_tool: BetaLocalFilesystemMemoryTool) -> None: sync_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="Line 1\nLine 2\nLine 3", path="/memories/view_test.txt", ) ) result = sync_local_filesystem_tool.view( BetaMemoryTool20250818ViewCommand(command="view", path="/memories/view_test.txt") ) assert ( result == "Here's the content of /memories/view_test.txt with line numbers:\n 1\tLine 1\n 2\tLine 2\n 3\tLine 3" ) def test_view_directory(self, sync_local_filesystem_tool: BetaLocalFilesystemMemoryTool) -> None: sync_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="File 1", path="/memories/dir/file1.txt", ) ) sync_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="File 2", path="/memories/dir/file2.txt", ) ) result = sync_local_filesystem_tool.view( BetaMemoryTool20250818ViewCommand(command="view", path="/memories/dir") ) assert "Here're the files and directories up to 2 levels deep in /memories/dir" in result assert "/memories/dir" in result assert "/memories/dir/file1.txt" in result assert "/memories/dir/file2.txt" in result def test_view_file_with_range(self, sync_local_filesystem_tool: BetaLocalFilesystemMemoryTool) -> None: sync_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="Line 1\nLine 2\nLine 3\nLine 4\nLine 5", path="/memories/range_test.txt", ) ) result = sync_local_filesystem_tool.view( BetaMemoryTool20250818ViewCommand(command="view", path="/memories/range_test.txt", view_range=[2, 4]) ) assert ( result == "Here's the content of /memories/range_test.txt with line numbers:\n 2\tLine 2\n 3\tLine 3\n 4\tLine 4" ) def test_view_error_for_non_existent_file(self, sync_local_filesystem_tool: BetaLocalFilesystemMemoryTool) -> None: with pytest.raises( ToolError, match="The path /memories/nonexistent.txt does not exist. Please provide a valid path." ): sync_local_filesystem_tool.view( BetaMemoryTool20250818ViewCommand(command="view", path="/memories/nonexistent.txt") ) def test_view_error_for_files_with_too_many_lines( self, sync_local_filesystem_tool: BetaLocalFilesystemMemoryTool ) -> None: too_many_lines = "\n".join(["line"] * 1000000) sync_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text=too_many_lines, path="/memories/huge.txt", ) ) with pytest.raises(ToolError, match=r"exceeds maximum line limit of 999,999 lines"): sync_local_filesystem_tool.view( BetaMemoryTool20250818ViewCommand(command="view", path="/memories/huge.txt") ) def test_str_replace(self, sync_local_filesystem_tool: BetaLocalFilesystemMemoryTool) -> None: sync_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="Line 1\nOld Text\nLine 3", path="/memories/replace_test.txt", ) ) result = sync_local_filesystem_tool.str_replace( BetaMemoryTool20250818StrReplaceCommand( command="str_replace", path="/memories/replace_test.txt", old_str="Old Text", new_str="New Text", ) ) assert ( result == "The memory file has been edited. Here is the snippet showing the change (with line numbers):\n 1\tLine 1\n 2\tNew Text\n 3\tLine 3" ) dir_snapshot = get_directory_snapshot(str(sync_local_filesystem_tool.base_path)) assert dir_snapshot == {"memories/replace_test.txt": "Line 1\nNew Text\nLine 3"} def test_str_replace_error_when_string_not_found( self, sync_local_filesystem_tool: BetaLocalFilesystemMemoryTool ) -> None: sync_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="Hello, World!", path="/memories/replace_test.txt", ) ) with pytest.raises( ToolError, match="No replacement was performed, old_str `NotFound` did not appear verbatim in /memories/replace_test.txt.", ): sync_local_filesystem_tool.str_replace( BetaMemoryTool20250818StrReplaceCommand( command="str_replace", path="/memories/replace_test.txt", old_str="NotFound", new_str="Python", ) ) def test_str_replace_error_when_string_appears_multiple_times( self, sync_local_filesystem_tool: BetaLocalFilesystemMemoryTool ) -> None: sync_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="Hello\nMiddle\nHello", path="/memories/replace_test.txt", ) ) with pytest.raises( ToolError, match=r"No replacement was performed\. Multiple occurrences of old_str `Hello` in lines: 1, 3\. Please ensure it is unique", ): sync_local_filesystem_tool.str_replace( BetaMemoryTool20250818StrReplaceCommand( command="str_replace", path="/memories/replace_test.txt", old_str="Hello", new_str="Hi", ) ) def test_str_replace_error_for_non_existent_file( self, sync_local_filesystem_tool: BetaLocalFilesystemMemoryTool ) -> None: with pytest.raises( ToolError, match="The path /memories/nonexistent.txt does not exist. Please provide a valid path." ): sync_local_filesystem_tool.str_replace( BetaMemoryTool20250818StrReplaceCommand( command="str_replace", path="/memories/nonexistent.txt", old_str="old", new_str="new", ) ) def test_insert(self, sync_local_filesystem_tool: BetaLocalFilesystemMemoryTool) -> None: sync_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="Line 1\nLine 2", path="/memories/insert_test.txt" ) ) result = sync_local_filesystem_tool.insert( BetaMemoryTool20250818InsertCommand( command="insert", path="/memories/insert_test.txt", insert_line=1, insert_text="Inserted Line" ) ) assert result == "The file /memories/insert_test.txt has been edited." dir_snapshot = get_directory_snapshot(str(sync_local_filesystem_tool.base_path)) assert dir_snapshot == {"memories/insert_test.txt": "Line 1\nInserted Line\nLine 2\n"} def test_insert_at_beginning_of_file(self, sync_local_filesystem_tool: BetaLocalFilesystemMemoryTool) -> None: sync_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="Line 1\nLine 2", path="/memories/insert_test.txt" ) ) sync_local_filesystem_tool.insert( BetaMemoryTool20250818InsertCommand( command="insert", path="/memories/insert_test.txt", insert_line=0, insert_text="First Line" ) ) dir_snapshot = get_directory_snapshot(str(sync_local_filesystem_tool.base_path)) assert dir_snapshot == {"memories/insert_test.txt": "First Line\nLine 1\nLine 2\n"} def test_insert_at_end_of_file(self, sync_local_filesystem_tool: BetaLocalFilesystemMemoryTool) -> None: sync_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="Line 1\nLine 2", path="/memories/insert_test.txt" ) ) sync_local_filesystem_tool.insert( BetaMemoryTool20250818InsertCommand( command="insert", path="/memories/insert_test.txt", insert_line=2, insert_text="Last Line" ) ) dir_snapshot = get_directory_snapshot(str(sync_local_filesystem_tool.base_path)) assert dir_snapshot == {"memories/insert_test.txt": "Line 1\nLine 2\nLast Line\n"} def test_insert_error_for_non_existent_file( self, sync_local_filesystem_tool: BetaLocalFilesystemMemoryTool ) -> None: with pytest.raises( ToolError, match="The path /memories/nonexistent.txt does not exist. Please provide a valid path." ): sync_local_filesystem_tool.insert( BetaMemoryTool20250818InsertCommand( command="insert", path="/memories/nonexistent.txt", insert_line=0, insert_text="text" ) ) def test_insert_error_for_invalid_insert_line( self, sync_local_filesystem_tool: BetaLocalFilesystemMemoryTool ) -> None: sync_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="Line 1\nLine 2", path="/memories/insert_test.txt" ) ) with pytest.raises( ToolError, match=r"Invalid `insert_line` parameter: 10. It should be within the range \[0, 2\]", ): sync_local_filesystem_tool.insert( BetaMemoryTool20250818InsertCommand( command="insert", path="/memories/insert_test.txt", insert_line=10, insert_text="text" ) ) def test_delete(self, sync_local_filesystem_tool: BetaLocalFilesystemMemoryTool) -> None: sync_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="To be deleted", path="/memories/delete_me.txt" ) ) result = sync_local_filesystem_tool.delete( BetaMemoryTool20250818DeleteCommand(command="delete", path="/memories/delete_me.txt") ) assert result == "Successfully deleted /memories/delete_me.txt" dir_snapshot = get_directory_snapshot(str(sync_local_filesystem_tool.base_path)) assert dir_snapshot == {} def test_delete_directory(self, sync_local_filesystem_tool: BetaLocalFilesystemMemoryTool) -> None: sync_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand(command="create", file_text="Content", path="/memories/subdir/file.txt") ) result = sync_local_filesystem_tool.delete( BetaMemoryTool20250818DeleteCommand(command="delete", path="/memories/subdir") ) assert result == "Successfully deleted /memories/subdir" dir_snapshot = get_directory_snapshot(str(sync_local_filesystem_tool.base_path)) assert dir_snapshot == {} def test_delete_error_when_file_not_found(self, sync_local_filesystem_tool: BetaLocalFilesystemMemoryTool) -> None: with pytest.raises(ToolError, match="The path /memories/nonexistent.txt does not exist"): sync_local_filesystem_tool.delete( BetaMemoryTool20250818DeleteCommand(command="delete", path="/memories/nonexistent.txt") ) def test_delete_not_allow_deleting_memories_directory( self, sync_local_filesystem_tool: BetaLocalFilesystemMemoryTool ) -> None: with pytest.raises(ToolError, match="Cannot delete the /memories directory itself"): sync_local_filesystem_tool.delete(BetaMemoryTool20250818DeleteCommand(command="delete", path="/memories")) def test_rename(self, sync_local_filesystem_tool: BetaLocalFilesystemMemoryTool) -> None: sync_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="Original content", path="/memories/old_name.txt" ) ) result = sync_local_filesystem_tool.rename( BetaMemoryTool20250818RenameCommand( command="rename", old_path="/memories/old_name.txt", new_path="/memories/new_name.txt" ) ) assert result == "Successfully renamed /memories/old_name.txt to /memories/new_name.txt" dir_snapshot = get_directory_snapshot(str(sync_local_filesystem_tool.base_path)) assert dir_snapshot == {"memories/new_name.txt": "Original content"} def test_rename_to_nested_path(self, sync_local_filesystem_tool: BetaLocalFilesystemMemoryTool) -> None: sync_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand(command="create", file_text="Content", path="/memories/file.txt") ) result = sync_local_filesystem_tool.rename( BetaMemoryTool20250818RenameCommand( command="rename", old_path="/memories/file.txt", new_path="/memories/nested/dir/file.txt" ) ) assert result == "Successfully renamed /memories/file.txt to /memories/nested/dir/file.txt" dir_snapshot = get_directory_snapshot(str(sync_local_filesystem_tool.base_path)) assert dir_snapshot == {"memories/nested/dir/file.txt": "Content"} def test_rename_error_when_source_not_found( self, sync_local_filesystem_tool: BetaLocalFilesystemMemoryTool ) -> None: with pytest.raises(ToolError, match="The path /memories/nonexistent.txt does not exist"): sync_local_filesystem_tool.rename( BetaMemoryTool20250818RenameCommand( command="rename", old_path="/memories/nonexistent.txt", new_path="/memories/new.txt" ) ) def test_rename_error_when_destination_exists( self, sync_local_filesystem_tool: BetaLocalFilesystemMemoryTool ) -> None: sync_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand(command="create", file_text="File 1", path="/memories/file1.txt") ) sync_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand(command="create", file_text="File 2", path="/memories/file2.txt") ) with pytest.raises(ToolError, match="The destination /memories/file2.txt already exists"): sync_local_filesystem_tool.rename( BetaMemoryTool20250818RenameCommand( command="rename", old_path="/memories/file1.txt", new_path="/memories/file2.txt" ) ) def test_path_validation_reject_paths_not_starting_with_memories( self, sync_local_filesystem_tool: BetaLocalFilesystemMemoryTool ) -> None: with pytest.raises(ToolError, match="Path must start with /memories"): sync_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand(command="create", file_text="Invalid", path="/invalid/path.txt") ) def test_path_validation_reject_paths_trying_to_escape_memories( self, sync_local_filesystem_tool: BetaLocalFilesystemMemoryTool ) -> None: with pytest.raises(ToolError, match="Path /memories/../../../etc/passwd would escape /memories directory"): sync_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="Escape attempt", path="/memories/../../../etc/passwd" ) ) def test_symlink_validation_reject_symlink_pointing_outside_memories( self, sync_local_filesystem_tool: BetaLocalFilesystemMemoryTool ) -> None: with tempfile.TemporaryDirectory() as outside_dir: Path(outside_dir, "secret.txt").write_text("sensitive data", encoding="utf-8") memories_path = sync_local_filesystem_tool.memory_root symlink_path = memories_path / "escape_link" os.symlink(outside_dir, symlink_path, target_is_directory=True) with pytest.raises(ToolError, match="Path .* would escape /memories directory"): sync_local_filesystem_tool.view( BetaMemoryTool20250818ViewCommand(command="view", path="/memories/escape_link/secret.txt") ) def test_symlink_validation_reject_creating_files_through_symlink_pointing_outside( self, sync_local_filesystem_tool: BetaLocalFilesystemMemoryTool ) -> None: with tempfile.TemporaryDirectory() as outside_dir: memories_path = sync_local_filesystem_tool.memory_root symlink_path = memories_path / "bad_link" os.symlink(outside_dir, symlink_path, target_is_directory=True) with pytest.raises(ToolError, match="Path .* would escape /memories directory"): sync_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="malicious content", path="/memories/bad_link/hacked.txt" ) ) def test_symlink_validation_reject_parent_directory_that_is_symlink_pointing_outside( self, sync_local_filesystem_tool: BetaLocalFilesystemMemoryTool ) -> None: with tempfile.TemporaryDirectory() as outside_dir: memories_path = sync_local_filesystem_tool.memory_root symlink_dir_path = memories_path / "subdir" os.symlink(outside_dir, symlink_dir_path, target_is_directory=True) with pytest.raises(ToolError, match="Path .* would escape /memories directory"): sync_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="content", path="/memories/subdir/nested/file.txt" ) ) class TestBetaAsyncLocalFilesystemMemoryTool: async def test_mkdir_parents_not_world_writable_under_permissive_umask(self) -> None: if os.name != "posix": pytest.skip("POSIX mode bits only") old_umask = os.umask(0) try: with tempfile.TemporaryDirectory() as tmp: base_path = Path(tmp) / "nested" / "memory" tool = BetaAsyncLocalFilesystemMemoryTool(base_path=str(base_path)) await tool._ensure_memory_root() # triggers lazy directory creation memory_root = Path(str(tool.memory_root)) for directory in (base_path.parent, base_path, memory_root): mode = stat.S_IMODE(directory.stat().st_mode) assert mode == 0o700, f"{directory} has mode {oct(mode)}, expected 0o700" assert not (mode & 0o077), f"{directory} is group/other-accessible: {oct(mode)}" finally: os.umask(old_umask) async def test_create(self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool) -> None: result = await async_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="Hello, World!", path="/memories/test_file.txt", ) ) assert result == "File created successfully at: /memories/test_file.txt" dir_snapshot = get_directory_snapshot(str(async_local_filesystem_tool.base_path)) assert dir_snapshot == {"memories/test_file.txt": "Hello, World!"} async def test_create_nested_directories( self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool ) -> None: result = await async_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="Nested file", path="/memories/deep/nested/file.txt", ) ) assert result == "File created successfully at: /memories/deep/nested/file.txt" dir_snapshot = get_directory_snapshot(str(async_local_filesystem_tool.base_path)) assert dir_snapshot == {"memories/deep/nested/file.txt": "Nested file"} async def test_create_error_if_file_already_exists( self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool ) -> None: await async_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="Original", path="/memories/existing.txt", ) ) with pytest.raises(ToolError, match="File /memories/existing.txt already exists"): await async_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="New", path="/memories/existing.txt", ) ) async def test_view_file(self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool) -> None: await async_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="Line 1\nLine 2\nLine 3", path="/memories/view_test.txt", ) ) result = await async_local_filesystem_tool.view( BetaMemoryTool20250818ViewCommand(command="view", path="/memories/view_test.txt") ) assert ( result == "Here's the content of /memories/view_test.txt with line numbers:\n 1\tLine 1\n 2\tLine 2\n 3\tLine 3" ) async def test_view_directory(self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool) -> None: await async_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="File 1", path="/memories/dir/file1.txt", ) ) await async_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="File 2", path="/memories/dir/file2.txt", ) ) result = await async_local_filesystem_tool.view( BetaMemoryTool20250818ViewCommand(command="view", path="/memories/dir") ) assert "Here're the files and directories up to 2 levels deep in /memories/dir" in result assert "/memories/dir" in result assert "/memories/dir/file1.txt" in result assert "/memories/dir/file2.txt" in result async def test_view_file_with_range(self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool) -> None: await async_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="Line 1\nLine 2\nLine 3\nLine 4\nLine 5", path="/memories/range_test.txt", ) ) result = await async_local_filesystem_tool.view( BetaMemoryTool20250818ViewCommand(command="view", path="/memories/range_test.txt", view_range=[2, 4]) ) assert ( result == "Here's the content of /memories/range_test.txt with line numbers:\n 2\tLine 2\n 3\tLine 3\n 4\tLine 4" ) async def test_view_error_for_non_existent_file( self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool ) -> None: with pytest.raises( ToolError, match="The path /memories/nonexistent.txt does not exist. Please provide a valid path." ): await async_local_filesystem_tool.view( BetaMemoryTool20250818ViewCommand(command="view", path="/memories/nonexistent.txt") ) async def test_view_error_for_files_with_too_many_lines( self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool ) -> None: too_many_lines = "\n".join(["line"] * 1000000) await async_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text=too_many_lines, path="/memories/huge.txt", ) ) with pytest.raises(ToolError, match=r"exceeds maximum line limit of 999,999 lines"): await async_local_filesystem_tool.view( BetaMemoryTool20250818ViewCommand(command="view", path="/memories/huge.txt") ) async def test_str_replace(self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool) -> None: await async_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="Line 1\nOld Text\nLine 3", path="/memories/replace_test.txt", ) ) result = await async_local_filesystem_tool.str_replace( BetaMemoryTool20250818StrReplaceCommand( command="str_replace", path="/memories/replace_test.txt", old_str="Old Text", new_str="New Text", ) ) assert ( result == "The memory file has been edited. Here is the snippet showing the change (with line numbers):\n 1\tLine 1\n 2\tNew Text\n 3\tLine 3" ) dir_snapshot = get_directory_snapshot(str(async_local_filesystem_tool.base_path)) assert dir_snapshot == {"memories/replace_test.txt": "Line 1\nNew Text\nLine 3"} async def test_str_replace_error_when_string_not_found( self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool ) -> None: await async_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="Hello, World!", path="/memories/replace_test.txt", ) ) with pytest.raises( ToolError, match="No replacement was performed, old_str `NotFound` did not appear verbatim in /memories/replace_test.txt.", ): await async_local_filesystem_tool.str_replace( BetaMemoryTool20250818StrReplaceCommand( command="str_replace", path="/memories/replace_test.txt", old_str="NotFound", new_str="Python", ) ) async def test_str_replace_error_when_string_appears_multiple_times( self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool ) -> None: await async_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="Hello\nMiddle\nHello", path="/memories/replace_test.txt", ) ) with pytest.raises( ToolError, match=r"No replacement was performed\. Multiple occurrences of old_str `Hello` in lines: 1, 3\. Please ensure it is unique", ): await async_local_filesystem_tool.str_replace( BetaMemoryTool20250818StrReplaceCommand( command="str_replace", path="/memories/replace_test.txt", old_str="Hello", new_str="Hi", ) ) async def test_str_replace_error_for_non_existent_file( self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool ) -> None: with pytest.raises( ToolError, match="The path /memories/nonexistent.txt does not exist. Please provide a valid path." ): await async_local_filesystem_tool.str_replace( BetaMemoryTool20250818StrReplaceCommand( command="str_replace", path="/memories/nonexistent.txt", old_str="old", new_str="new", ) ) async def test_insert(self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool) -> None: await async_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="Line 1\nLine 2", path="/memories/insert_test.txt" ) ) result = await async_local_filesystem_tool.insert( BetaMemoryTool20250818InsertCommand( command="insert", path="/memories/insert_test.txt", insert_line=1, insert_text="Inserted Line" ) ) assert result == "The file /memories/insert_test.txt has been edited." dir_snapshot = get_directory_snapshot(str(async_local_filesystem_tool.base_path)) assert dir_snapshot == {"memories/insert_test.txt": "Line 1\nInserted Line\nLine 2\n"} async def test_insert_at_beginning_of_file( self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool ) -> None: await async_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="Line 1\nLine 2", path="/memories/insert_test.txt" ) ) await async_local_filesystem_tool.insert( BetaMemoryTool20250818InsertCommand( command="insert", path="/memories/insert_test.txt", insert_line=0, insert_text="First Line" ) ) dir_snapshot = get_directory_snapshot(str(async_local_filesystem_tool.base_path)) assert dir_snapshot == {"memories/insert_test.txt": "First Line\nLine 1\nLine 2\n"} async def test_insert_at_end_of_file(self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool) -> None: await async_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="Line 1\nLine 2", path="/memories/insert_test.txt" ) ) await async_local_filesystem_tool.insert( BetaMemoryTool20250818InsertCommand( command="insert", path="/memories/insert_test.txt", insert_line=2, insert_text="Last Line" ) ) dir_snapshot = get_directory_snapshot(str(async_local_filesystem_tool.base_path)) assert dir_snapshot == {"memories/insert_test.txt": "Line 1\nLine 2\nLast Line\n"} async def test_insert_error_for_non_existent_file( self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool ) -> None: with pytest.raises( ToolError, match="The path /memories/nonexistent.txt does not exist. Please provide a valid path." ): await async_local_filesystem_tool.insert( BetaMemoryTool20250818InsertCommand( command="insert", path="/memories/nonexistent.txt", insert_line=0, insert_text="text" ) ) async def test_insert_error_for_invalid_insert_line( self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool ) -> None: await async_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="Line 1\nLine 2", path="/memories/insert_test.txt" ) ) with pytest.raises( ToolError, match=r"Invalid `insert_line` parameter: 10\. It should be within the range \[0, 2\]", ): await async_local_filesystem_tool.insert( BetaMemoryTool20250818InsertCommand( command="insert", path="/memories/insert_test.txt", insert_line=10, insert_text="text" ) ) async def test_delete(self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool) -> None: await async_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="To be deleted", path="/memories/delete_me.txt" ) ) result = await async_local_filesystem_tool.delete( BetaMemoryTool20250818DeleteCommand(command="delete", path="/memories/delete_me.txt") ) assert result == "Successfully deleted /memories/delete_me.txt" dir_snapshot = get_directory_snapshot(str(async_local_filesystem_tool.base_path)) assert dir_snapshot == {} async def test_delete_directory(self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool) -> None: await async_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand(command="create", file_text="Content", path="/memories/subdir/file.txt") ) result = await async_local_filesystem_tool.delete( BetaMemoryTool20250818DeleteCommand(command="delete", path="/memories/subdir") ) assert result == "Successfully deleted /memories/subdir" dir_snapshot = get_directory_snapshot(str(async_local_filesystem_tool.base_path)) assert dir_snapshot == {} async def test_delete_error_when_file_not_found( self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool ) -> None: with pytest.raises(ToolError, match="The path /memories/nonexistent.txt does not exist"): await async_local_filesystem_tool.delete( BetaMemoryTool20250818DeleteCommand(command="delete", path="/memories/nonexistent.txt") ) async def test_delete_not_allow_deleting_memories_directory( self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool ) -> None: with pytest.raises(ToolError, match="Cannot delete the /memories directory itself"): await async_local_filesystem_tool.delete( BetaMemoryTool20250818DeleteCommand(command="delete", path="/memories") ) async def test_rename(self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool) -> None: await async_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="Original content", path="/memories/old_name.txt" ) ) result = await async_local_filesystem_tool.rename( BetaMemoryTool20250818RenameCommand( command="rename", old_path="/memories/old_name.txt", new_path="/memories/new_name.txt" ) ) assert result == "Successfully renamed /memories/old_name.txt to /memories/new_name.txt" dir_snapshot = get_directory_snapshot(str(async_local_filesystem_tool.base_path)) assert dir_snapshot == {"memories/new_name.txt": "Original content"} async def test_rename_to_nested_path(self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool) -> None: await async_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand(command="create", file_text="Content", path="/memories/file.txt") ) result = await async_local_filesystem_tool.rename( BetaMemoryTool20250818RenameCommand( command="rename", old_path="/memories/file.txt", new_path="/memories/nested/dir/file.txt" ) ) assert result == "Successfully renamed /memories/file.txt to /memories/nested/dir/file.txt" dir_snapshot = get_directory_snapshot(str(async_local_filesystem_tool.base_path)) assert dir_snapshot == {"memories/nested/dir/file.txt": "Content"} async def test_rename_error_when_source_not_found( self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool ) -> None: with pytest.raises(ToolError, match="The path /memories/nonexistent.txt does not exist"): await async_local_filesystem_tool.rename( BetaMemoryTool20250818RenameCommand( command="rename", old_path="/memories/nonexistent.txt", new_path="/memories/new.txt" ) ) async def test_rename_error_when_destination_exists( self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool ) -> None: await async_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand(command="create", file_text="File 1", path="/memories/file1.txt") ) await async_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand(command="create", file_text="File 2", path="/memories/file2.txt") ) with pytest.raises(ToolError, match="The destination /memories/file2.txt already exists"): await async_local_filesystem_tool.rename( BetaMemoryTool20250818RenameCommand( command="rename", old_path="/memories/file1.txt", new_path="/memories/file2.txt" ) ) async def test_path_validation_reject_paths_not_starting_with_memories( self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool ) -> None: with pytest.raises(ToolError, match="Path must start with /memories"): await async_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand(command="create", file_text="Invalid", path="/invalid/path.txt") ) async def test_path_validation_reject_paths_trying_to_escape_memories( self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool ) -> None: with pytest.raises(ToolError, match="Path /memories/../../../etc/passwd would escape /memories directory"): await async_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="Escape attempt", path="/memories/../../../etc/passwd" ) ) async def test_validate_path_returns_resolved_path_not_symlink_target( self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool ) -> None: """_validate_path must return the resolved path so that subsequent file operations hit the real location, not the (potentially swappable) symlink. Without this fix, an attacker could: 1. Create /memories/link -> /memories/legit (passes validation) 2. Swap /memories/link -> /etc between validation and the file operation 3. The file operation would follow the new symlink target """ memories_path = Path(str(async_local_filesystem_tool.memory_root)) memories_path.mkdir(parents=True, exist_ok=True) # Create a real directory inside memories and a symlink pointing to it legit_dir = memories_path / "legit" legit_dir.mkdir() (legit_dir / "file.txt").write_text("content", encoding="utf-8") link_path = memories_path / "link" os.symlink(legit_dir, link_path, target_is_directory=True) # _validate_path should return the resolved real path, not the symlink path result = await async_local_filesystem_tool._validate_path("/memories/link/file.txt") result_str = str(result) # The returned path should point to the resolved location (under legit/), # not through the symlink assert "link" not in result_str, f"_validate_path returned unresolved symlink path: {result_str}" assert str(legit_dir.resolve()) in result_str async def test_symlink_validation_reject_symlink_pointing_outside_memories( self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool ) -> None: with tempfile.TemporaryDirectory() as outside_dir: Path(outside_dir, "secret.txt").write_text("sensitive data", encoding="utf-8") memories_path = Path(str(async_local_filesystem_tool.memory_root)) memories_path.mkdir(parents=True, exist_ok=True) symlink_path = memories_path / "escape_link" os.symlink(outside_dir, symlink_path, target_is_directory=True) with pytest.raises(ToolError, match="Path .* would escape /memories directory"): await async_local_filesystem_tool.view( BetaMemoryTool20250818ViewCommand(command="view", path="/memories/escape_link/secret.txt") ) async def test_symlink_validation_reject_creating_files_through_symlink_pointing_outside( self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool ) -> None: with tempfile.TemporaryDirectory() as outside_dir: memories_path = Path(str(async_local_filesystem_tool.memory_root)) memories_path.mkdir(parents=True, exist_ok=True) symlink_path = memories_path / "bad_link" os.symlink(outside_dir, symlink_path, target_is_directory=True) with pytest.raises(ToolError, match="Path .* would escape /memories directory"): await async_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="malicious content", path="/memories/bad_link/hacked.txt" ) ) async def test_symlink_validation_reject_parent_directory_that_is_symlink_pointing_outside( self, async_local_filesystem_tool: BetaAsyncLocalFilesystemMemoryTool ) -> None: with tempfile.TemporaryDirectory() as outside_dir: memories_path = Path(str(async_local_filesystem_tool.memory_root)) memories_path.mkdir(parents=True, exist_ok=True) symlink_dir_path = memories_path / "subdir" os.symlink(outside_dir, symlink_dir_path, target_is_directory=True) with pytest.raises(ToolError, match="Path .* would escape /memories directory"): await async_local_filesystem_tool.create( BetaMemoryTool20250818CreateCommand( command="create", file_text="content", path="/memories/subdir/nested/file.txt" ) ) anthropic-sdk-python-0.120.2/tests/lib/tools/test_agent_toolset.py000066400000000000000000000612051523216435200253000ustar00rootroot00000000000000from __future__ import annotations import os import sys import base64 from typing import Any, cast from pathlib import Path from typing_extensions import Required, get_args, get_origin, get_type_hints import anyio import pytest from anthropic._compat import PYDANTIC_V1 from anthropic.lib.tools import ToolError from anthropic.lib.tools.agent_toolset import ( _BINARY_MEDIA_TYPES, DEFAULT_MAX_IMAGE_BASE64_BYTES, BashSession, AgentToolContext, resolve_path, beta_edit_tool, beta_glob_tool, beta_grep_tool, beta_read_tool, beta_write_tool, beta_agent_toolset_20260401, ) from anthropic.types.beta.beta_base64_pdf_source_param import BetaBase64PDFSourceParam from anthropic.types.beta.beta_base64_image_source_param import BetaBase64ImageSourceParam needs_pydantic_v2 = pytest.mark.skipif(PYDANTIC_V1, reason="tool functions are only supported with pydantic v2") @pytest.mark.parametrize( ("description", "p", "unrestricted", "expect_error"), [ ("relative path inside workdir resolves", "a/b.txt", False, False), ("dot-dot that stays inside workdir resolves", "a/../b.txt", False, False), ("dot-dot that escapes workdir is rejected", "../etc/passwd", False, True), ("absolute path outside workdir is rejected by default", "/etc/passwd", False, True), ("absolute path outside workdir is allowed when unrestricted_paths is set", "/etc/passwd", True, False), ], ) def test_resolve_path(tmp_path: Path, description: str, p: str, unrestricted: bool, expect_error: bool) -> None: env = AgentToolContext(workdir=str(tmp_path), unrestricted_paths=unrestricted) if expect_error: with pytest.raises(ValueError): resolve_path(env, p) else: assert resolve_path(env, p), description def test_resolve_path_absolute_inside_workdir(tmp_path: Path) -> None: """An absolute path that canonicalises inside the workdir is permitted in confined mode — the containment check is what guards the jail, not the absolute/relative spelling. Regression for anthropics/anthropic-sdk-go#368. """ env = AgentToolContext(workdir=str(tmp_path)) (tmp_path / "a.txt").write_text("x") assert resolve_path(env, str(tmp_path / "a.txt")) == tmp_path / "a.txt" assert resolve_path(env, str(tmp_path)) == tmp_path sub = tmp_path / "sub" sub.mkdir() assert resolve_path(env, str(sub / "b.txt")) == sub / "b.txt" # Still rejected: absolute outside, and absolute-that-symlinks-outside. with pytest.raises(ValueError, match="escapes workdir"): resolve_path(env, "/etc/passwd") if sys.platform != "win32": (tmp_path / "out").symlink_to("/etc/passwd") with pytest.raises(ValueError, match="escapes workdir"): resolve_path(env, str(tmp_path / "out")) def test_resolve_path_segment_aware_sibling(tmp_path: Path) -> None: """A sibling directory sharing a prefix (workdir vs workdir2) must not satisfy the jail.""" root = tmp_path / "work" root.mkdir() env = AgentToolContext(workdir=str(root)) with pytest.raises(ValueError): resolve_path(env, "../work2/file") @needs_pydantic_v2 def test_agent_toolset_names_and_type(tmp_path: Path) -> None: env = AgentToolContext(workdir=str(tmp_path)) names = [t.name for t in beta_agent_toolset_20260401(env)] assert names == ["bash", "read", "write", "edit", "glob", "grep"] @needs_pydantic_v2 async def test_agent_toolset_filter_and_extend(tmp_path: Path) -> None: """The list returned by beta_agent_toolset_20260401 is a plain list that callers can filter or extend.""" env = AgentToolContext(workdir=str(tmp_path)) subset = [t for t in beta_agent_toolset_20260401(env) if t.name not in ("bash", "grep")] assert [t.name for t in subset] == ["read", "write", "edit", "glob"] extended = [*subset, beta_read_tool(env)] assert extended[-1].name == "read" @needs_pydantic_v2 async def test_read_write_edit_roundtrip(tmp_path: Path) -> None: env = AgentToolContext(workdir=str(tmp_path)) msg = await beta_write_tool(env).call({"file_path": "f.txt", "content": "hello world"}) assert isinstance(msg, str) assert "wrote" in msg text = await beta_read_tool(env).call({"file_path": "f.txt"}) assert text == "hello world" await beta_edit_tool(env).call({"file_path": "f.txt", "old_string": "world", "new_string": "there"}) assert (tmp_path / "f.txt").read_text() == "hello there" @needs_pydantic_v2 async def test_read_view_range(tmp_path: Path) -> None: (tmp_path / "f.txt").write_text("a\nb\nc\nd\n") env = AgentToolContext(workdir=str(tmp_path)) out = await beta_read_tool(env).call({"file_path": "f.txt", "view_range": [2, 3]}) assert out == "b\nc" @needs_pydantic_v2 async def test_read_rejects_oversized_file(tmp_path: Path) -> None: (tmp_path / "big.txt").write_bytes(b"a" * (257 * 1024)) env = AgentToolContext(workdir=str(tmp_path)) with pytest.raises(ToolError, match="exceeds"): await beta_read_tool(env).call({"file_path": "big.txt"}) @needs_pydantic_v2 async def test_read_rejects_directory(tmp_path: Path) -> None: (tmp_path / "sub").mkdir() env = AgentToolContext(workdir=str(tmp_path)) with pytest.raises(ToolError, match="not a regular file"): await beta_read_tool(env).call({"file_path": "sub"}) @needs_pydantic_v2 async def test_edit_rejects_oversized_file(tmp_path: Path) -> None: (tmp_path / "big.txt").write_bytes(b"a" * (257 * 1024)) env = AgentToolContext(workdir=str(tmp_path)) with pytest.raises(ToolError, match="exceeds"): await beta_edit_tool(env).call({"file_path": "big.txt", "old_string": "a", "new_string": "b"}) @needs_pydantic_v2 async def test_edit_rejects_directory(tmp_path: Path) -> None: (tmp_path / "sub").mkdir() env = AgentToolContext(workdir=str(tmp_path)) with pytest.raises(ToolError, match="not a regular file"): await beta_edit_tool(env).call({"file_path": "sub", "old_string": "a", "new_string": "b"}) @needs_pydantic_v2 async def test_edit_normal_within_limit(tmp_path: Path) -> None: (tmp_path / "f.txt").write_text("hello world") env = AgentToolContext(workdir=str(tmp_path)) await beta_edit_tool(env).call({"file_path": "f.txt", "old_string": "world", "new_string": "there"}) assert (tmp_path / "f.txt").read_text() == "hello there" @needs_pydantic_v2 async def test_edit_custom_max_bytes_rejects_below_cap(tmp_path: Path) -> None: (tmp_path / "f.txt").write_bytes(b"OLD" + b"\x00" * 2000) env = AgentToolContext(workdir=str(tmp_path), max_file_bytes=1024) with pytest.raises(ToolError, match="exceeds"): await beta_edit_tool(env).call({"file_path": "f.txt", "old_string": "OLD", "new_string": "NEW"}) @needs_pydantic_v2 async def test_edit_custom_max_bytes_allows_above_default(tmp_path: Path) -> None: (tmp_path / "f.txt").write_bytes(b"OLD" + b"\x00" * (257 * 1024)) env = AgentToolContext(workdir=str(tmp_path), max_file_bytes=512 * 1024) await beta_edit_tool(env).call({"file_path": "f.txt", "old_string": "OLD", "new_string": "NEW"}) assert (tmp_path / "f.txt").read_bytes()[:3] == b"NEW" @needs_pydantic_v2 async def test_edit_uncapped_allows_oversized(tmp_path: Path) -> None: (tmp_path / "f.txt").write_bytes(b"OLD" + b"\x00" * (257 * 1024)) env = AgentToolContext(workdir=str(tmp_path), max_file_bytes=None) await beta_edit_tool(env).call({"file_path": "f.txt", "old_string": "OLD", "new_string": "NEW"}) @needs_pydantic_v2 async def test_edit_rejects_directory_even_when_uncapped(tmp_path: Path) -> None: (tmp_path / "sub").mkdir() env = AgentToolContext(workdir=str(tmp_path), max_file_bytes=None) with pytest.raises(ToolError, match="not a regular file"): await beta_edit_tool(env).call({"file_path": "sub", "old_string": "a", "new_string": "b"}) @needs_pydantic_v2 async def test_read_custom_max_bytes_rejects_below_cap(tmp_path: Path) -> None: (tmp_path / "f.txt").write_bytes(b"a" * 2000) env = AgentToolContext(workdir=str(tmp_path), max_file_bytes=1024) with pytest.raises(ToolError, match="exceeds"): await beta_read_tool(env).call({"file_path": "f.txt"}) @needs_pydantic_v2 async def test_read_uncapped_allows_oversized(tmp_path: Path) -> None: (tmp_path / "big.txt").write_bytes(b"a" * (257 * 1024)) env = AgentToolContext(workdir=str(tmp_path), max_file_bytes=None) await beta_read_tool(env).call({"file_path": "big.txt"}) @needs_pydantic_v2 async def test_read_rejects_directory_even_when_uncapped(tmp_path: Path) -> None: (tmp_path / "sub").mkdir() env = AgentToolContext(workdir=str(tmp_path), max_file_bytes=None) with pytest.raises(ToolError, match="not a regular file"): await beta_read_tool(env).call({"file_path": "sub"}) def _media_type_literal_values(typed_dict: type, key: str) -> set[str]: """Extract the values of a ``Required[Literal[...]]`` TypedDict field.""" hint = get_type_hints(typed_dict, include_extras=True)[key] if get_origin(hint) is Required: (hint,) = get_args(hint) values = get_args(hint) assert values, f"expected a Literal for {typed_dict.__name__}.{key}" return set(values) def test_binary_media_types_track_generated_api_types() -> None: """Pin the read tool's extension map to the codegen'd media-type literals. When a spec change adds or removes a supported image/document media type, this fails to force the extension map (and its size caps) to be revisited. """ supported = _media_type_literal_values(BetaBase64ImageSourceParam, "media_type") | _media_type_literal_values( BetaBase64PDFSourceParam, "media_type" ) assert set(_BINARY_MEDIA_TYPES.values()) == supported # Real magic bytes so the round-trip assertion exercises non-UTF-8 data; the # tool itself only sniffs the extension. _JPEG_BYTES = b"\xff\xd8\xff\xe0" + b"\x00" * 32 _PNG_BYTES = b"\x89PNG\r\n\x1a\n" + b"\x00" * 32 _PDF_BYTES = b"%PDF-1.4\n%\xe2\xe3\xcf\xd3\n" @needs_pydantic_v2 @pytest.mark.parametrize( ("filename", "data", "kind", "media_type"), [ ("slide.jpg", _JPEG_BYTES, "image", "image/jpeg"), ("slide.JPEG", _JPEG_BYTES, "image", "image/jpeg"), ("chart.png", _PNG_BYTES, "image", "image/png"), ("doc.pdf", _PDF_BYTES, "document", "application/pdf"), ], ) async def test_read_binary_returns_content_block( tmp_path: Path, filename: str, data: bytes, kind: str, media_type: str ) -> None: """Images/PDFs come back as base64 content blocks, not UnicodeDecodeError.""" (tmp_path / filename).write_bytes(data) env = AgentToolContext(workdir=str(tmp_path)) result = await beta_read_tool(env).call({"file_path": filename}) assert not isinstance(result, str) (block,) = [cast("dict[str, Any]", b) for b in result] assert block["type"] == kind source = block["source"] assert source["type"] == "base64" assert source["media_type"] == media_type assert base64.standard_b64decode(source["data"]) == data @needs_pydantic_v2 async def test_read_binary_not_subject_to_text_cap(tmp_path: Path) -> None: """An image over the 256 KiB text default still reads (the API media caps govern).""" (tmp_path / "big.png").write_bytes(_PNG_BYTES + b"\x00" * (300 * 1024)) env = AgentToolContext(workdir=str(tmp_path)) result = await beta_read_tool(env).call({"file_path": "big.png"}) assert not isinstance(result, str) (block,) = [cast("dict[str, Any]", b) for b in result] assert block["type"] == "image" @needs_pydantic_v2 async def test_read_binary_honors_explicit_cap(tmp_path: Path) -> None: (tmp_path / "big.png").write_bytes(_PNG_BYTES + b"\x00" * 2048) env = AgentToolContext(workdir=str(tmp_path), max_file_bytes=1024) with pytest.raises(ToolError, match="exceeds"): await beta_read_tool(env).call({"file_path": "big.png"}) @needs_pydantic_v2 async def test_read_binary_rejects_over_media_cap(tmp_path: Path) -> None: """An image whose base64 form would exceed the default per-image cap is rejected up front.""" raw_cap = (DEFAULT_MAX_IMAGE_BASE64_BYTES // 4) * 3 (tmp_path / "huge.png").write_bytes(b"\x00" * (raw_cap + 1)) env = AgentToolContext(workdir=str(tmp_path), max_file_bytes=None) with pytest.raises(ToolError, match="exceeds"): await beta_read_tool(env).call({"file_path": "huge.png"}) @needs_pydantic_v2 async def test_read_binary_custom_media_caps(tmp_path: Path) -> None: """The media caps are configurable: a small custom cap rejects, a larger/disabled one permits.""" (tmp_path / "img.png").write_bytes(_PNG_BYTES + b"\x00" * 2048) # ~2 KiB raw (tmp_path / "doc.pdf").write_bytes(_PDF_BYTES + b"\x00" * 2048) # The image cap is on the base64 form (4/3 of raw), so 1 KiB rejects ~2 KiB raw. tight = AgentToolContext(workdir=str(tmp_path), max_image_base64_bytes=1024, max_pdf_bytes=1024) with pytest.raises(ToolError, match="exceeds"): await beta_read_tool(tight).call({"file_path": "img.png"}) with pytest.raises(ToolError, match="exceeds"): await beta_read_tool(tight).call({"file_path": "doc.pdf"}) # A larger cap (or ``None`` to disable) admits the same files. loose = AgentToolContext(workdir=str(tmp_path), max_image_base64_bytes=1024 * 1024, max_pdf_bytes=None) for name, kind in (("img.png", "image"), ("doc.pdf", "document")): result = await beta_read_tool(loose).call({"file_path": name}) assert not isinstance(result, str) (block,) = [cast("dict[str, Any]", b) for b in result] assert block["type"] == kind @needs_pydantic_v2 async def test_read_binary_rejects_view_range(tmp_path: Path) -> None: (tmp_path / "slide.jpg").write_bytes(_JPEG_BYTES) env = AgentToolContext(workdir=str(tmp_path)) with pytest.raises(ToolError, match="view_range is not supported"): await beta_read_tool(env).call({"file_path": "slide.jpg", "view_range": [1, 2]}) @needs_pydantic_v2 async def test_read_undecodable_binary_raises_tool_error(tmp_path: Path) -> None: """Non-image/PDF binary surfaces a clean ToolError, not a raw UnicodeDecodeError.""" (tmp_path / "blob.bin").write_bytes(b"\xff\xfe\x00\x01") env = AgentToolContext(workdir=str(tmp_path)) with pytest.raises(ToolError, match="not valid UTF-8"): await beta_read_tool(env).call({"file_path": "blob.bin"}) @needs_pydantic_v2 async def test_edit_binary_raises_tool_error(tmp_path: Path) -> None: (tmp_path / "blob.bin").write_bytes(b"\xff\xfe\x00\x01") env = AgentToolContext(workdir=str(tmp_path)) with pytest.raises(ToolError, match="not valid UTF-8"): await beta_edit_tool(env).call({"file_path": "blob.bin", "old_string": "a", "new_string": "b"}) @needs_pydantic_v2 def test_text_io_is_utf8_under_ascii_locale(tmp_path: Path) -> None: """Text I/O is explicitly UTF-8: an ASCII-locale host (LANG=C) must not mislabel valid UTF-8 as binary. Runs in a subprocess because the locale default is fixed at interpreter startup.""" import subprocess (tmp_path / "notes.txt").write_bytes("café — naïve\n".encode("utf-8")) script = "\n".join( [ "import sys, anyio", "from anthropic.lib.tools.agent_toolset import (", " AgentToolContext, beta_edit_tool, beta_read_tool)", f"ctx = AgentToolContext(workdir={str(tmp_path)!r})", "async def main():", " text = await beta_read_tool(ctx).call({'file_path': 'notes.txt'})", " sys.stdout.buffer.write(text.encode('utf-8'))", " await beta_edit_tool(ctx).call(", " {'file_path': 'notes.txt', 'old_string': 'caf\\u00e9', 'new_string': 'th\\u00e9'})", "anyio.run(main)", ] ) proc = subprocess.run( [sys.executable, "-c", script], env={**os.environ, "LC_ALL": "C", "LANG": "C", "PYTHONUTF8": "0", "PYTHONCOERCECLOCALE": "0"}, capture_output=True, ) assert proc.returncode == 0, proc.stderr.decode("utf-8", errors="replace") assert proc.stdout.decode("utf-8") == "café — naïve\n" assert (tmp_path / "notes.txt").read_text(encoding="utf-8") == "thé — naïve\n" @pytest.mark.parametrize( ("description", "args", "want_error"), [ ( "edit fails when old_string is absent from the file", {"file_path": "f.txt", "old_string": "nope", "new_string": "x"}, True, ), ( "edit fails when old_string is non-unique and replace_all is false", {"file_path": "f.txt", "old_string": "ab", "new_string": "x"}, True, ), ( "edit succeeds on non-unique old_string when replace_all is true", {"file_path": "f.txt", "old_string": "ab", "new_string": "x", "replace_all": True}, False, ), ], ) @needs_pydantic_v2 async def test_edit_uniqueness(tmp_path: Path, description: str, args: dict[str, object], want_error: bool) -> None: (tmp_path / "f.txt").write_text("ab ab") env = AgentToolContext(workdir=str(tmp_path)) tool = beta_edit_tool(env) if want_error: with pytest.raises(ToolError): await tool.call(args) else: assert await tool.call(args), description @needs_pydantic_v2 async def test_glob_mtime_order(tmp_path: Path) -> None: a = tmp_path / "a.txt" b = tmp_path / "b.txt" a.write_text("a") b.write_text("b") os.utime(a, (1, 1)) os.utime(b, (2, 2)) env = AgentToolContext(workdir=str(tmp_path)) res = await beta_glob_tool(env).call({"pattern": "*.txt"}) assert isinstance(res, str) lines = res.splitlines() assert lines[0].endswith("b.txt") and lines[1].endswith("a.txt") @needs_pydantic_v2 async def test_glob_with_path(tmp_path: Path) -> None: (tmp_path / "sub").mkdir() (tmp_path / "sub" / "a.txt").write_text("a") (tmp_path / "b.txt").write_text("b") env = AgentToolContext(workdir=str(tmp_path)) res = await beta_glob_tool(env).call({"pattern": "*.txt", "path": "sub"}) assert isinstance(res, str) assert res.endswith("a.txt") assert "b.txt" not in res @needs_pydantic_v2 async def test_grep_finds_match(tmp_path: Path) -> None: (tmp_path / "x.txt").write_text("foo\nbar\nbaz\n") env = AgentToolContext(workdir=str(tmp_path)) res = await beta_grep_tool(env).call({"pattern": "ba.", "path": "."}) assert isinstance(res, str) assert "bar" in res and "baz" in res @needs_pydantic_v2 async def test_grep_single_file_path(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: """Fallback walker must handle a file path, not just directories.""" (tmp_path / "x.txt").write_text("alpha\nbeta\n") env = AgentToolContext(workdir=str(tmp_path)) monkeypatch.setattr("shutil.which", lambda _name: None) # type: ignore[arg-type] res = await beta_grep_tool(env).call({"pattern": "beta", "path": "x.txt"}) assert isinstance(res, str) assert "beta" in res assert res != "no matches" @pytest.mark.skipif(sys.platform == "win32", reason="bash session requires /bin/bash") async def test_bash_session_persistence(tmp_path: Path) -> None: s = await BashSession.start(str(tmp_path)) try: out, code = await s.exec("export FOO=bar; echo set") assert (out, code) == ("set", 0) out, code = await s.exec("echo $FOO") assert (out, code) == ("bar", 0) finally: await s.close() @pytest.mark.skipif(sys.platform == "win32", reason="bash session requires /bin/bash") async def test_bash_timeout(tmp_path: Path) -> None: s = await BashSession.start(str(tmp_path)) with pytest.raises(TimeoutError): await s.exec("sleep 5", timeout=0.2) @pytest.mark.skipif(sys.platform == "win32", reason="bash session requires /bin/bash") async def test_bash_sentinel_not_spoofable(tmp_path: Path) -> None: """A command that prints a hardcoded marker can't truncate output or spoof the exit code.""" s = await BashSession.start(str(tmp_path)) try: out, code = await s.exec("printf '__ANT_CMD_DONE__7\\nafter\\n'; (exit 3)") assert "__ANT_CMD_DONE__7" in out assert "after" in out assert code == 3 finally: await s.close() @pytest.mark.skipif(sys.platform == "win32", reason="bash session requires /bin/bash") async def test_bash_stdin_redirect(tmp_path: Path) -> None: """A stdin-reading command gets immediate EOF instead of hanging until timeout.""" s = await BashSession.start(str(tmp_path)) try: out, code = await s.exec("cat; echo done", timeout=2.0) assert out == "done" assert code == 0 finally: await s.close() @pytest.mark.skipif(sys.platform == "win32", reason="bash session requires /bin/bash") async def test_bash_session_closed_property(tmp_path: Path) -> None: """``closed`` is the inverse of the old ``alive`` (TS parity) and there is no ``alive`` attribute any more.""" s = await BashSession.start(str(tmp_path)) assert s.closed is False assert not hasattr(s, "alive") await s.close() assert s.closed is True # A closed session refuses further commands rather than silently hanging. with pytest.raises(RuntimeError, match="terminated"): await s.exec("echo nope") @pytest.mark.skipif(sys.platform == "win32", reason="bash session requires /bin/bash") async def test_bash_outer_cancel_closes_subprocess_no_stale_state(tmp_path: Path) -> None: """Regression: a cancellation from an *outer* scope (e.g. the session runner's ``TOOL_TIMEOUT``) during a bash exec must tear the subprocess down, so the next call can't read the cancelled command's stale output/sentinel. anyio raises an outer-scope cancel as a plain ``Cancelled`` (not ``TimeoutError``), so the ``except TimeoutError`` cleanup never runs — only the new ``except get_cancelled_exc_class()`` path saves us here. """ s = await BashSession.start(str(tmp_path)) try: # The inner per-call timeout is huge; an OUTER scope cancels first, # mid-command, while `sleep` is producing no output. with anyio.move_on_after(0.3): await s.exec("sleep 5; echo STALE_MARKER", timeout=120.0) # The outer cancel fired mid-exec. The subprocess must have been closed # (not left alive with `sleep 5; echo STALE_MARKER` still queued). assert s.closed is True # And because it's closed, the next exec refuses outright — it can NOT # hand back the previous command's STALE_MARKER output + old sentinel. with pytest.raises(RuntimeError, match="terminated"): await s.exec("echo NEXT") finally: await s.close() @needs_pydantic_v2 async def test_read_through_symlink_escape_is_rejected(tmp_path: Path) -> None: """resolve_path realpaths, so a symlink that escapes the workdir is caught.""" outside = tmp_path / "outside" outside.mkdir() (outside / "secret.txt").write_text("secret") work = tmp_path / "work" work.mkdir() (work / "escape").symlink_to(outside) env = AgentToolContext(workdir=str(work)) with pytest.raises(ToolError, match="escapes workdir"): await beta_read_tool(env).call({"file_path": "escape/secret.txt"}) @needs_pydantic_v2 async def test_glob_rejects_dotdot_pattern(tmp_path: Path) -> None: """``Path.glob`` honours literal ``..`` segments — the tool must reject a pattern that would walk out of the workdir before it ever runs.""" outside = tmp_path / "outside" outside.mkdir() (outside / "secret.txt").write_text("secret") work = tmp_path / "work" work.mkdir() env = AgentToolContext(workdir=str(work)) with pytest.raises(ToolError, match=r"\.\."): await beta_glob_tool(env).call({"pattern": "../outside/*.txt"}) @needs_pydantic_v2 async def test_glob_post_filters_symlink_escape(tmp_path: Path) -> None: """A symlink traversed mid-pattern must not let a glob result escape the workdir.""" outside = tmp_path / "outside" outside.mkdir() (outside / "secret.txt").write_text("secret") work = tmp_path / "work" work.mkdir() (work / "escape").symlink_to(outside) env = AgentToolContext(workdir=str(work)) res = await beta_glob_tool(env).call({"pattern": "escape/*.txt"}) assert res == "no matches" @needs_pydantic_v2 async def test_grep_skips_symlinked_files(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: """The fallback walker must not read through a symlink that escapes the workdir.""" secret = tmp_path / "secret.txt" secret.write_text("TOPSECRET") work = tmp_path / "work" work.mkdir() (work / "leak").symlink_to(secret) (work / "real.txt").write_text("ordinary\n") env = AgentToolContext(workdir=str(work)) monkeypatch.setattr("shutil.which", lambda _name: None) # type: ignore[arg-type] res = await beta_grep_tool(env).call({"pattern": "TOPSECRET"}) assert res == "no matches" anthropic-sdk-python-0.120.2/tests/lib/tools/test_functions.py000066400000000000000000000513101523216435200244350ustar00rootroot00000000000000from __future__ import annotations from typing import Any, cast from contextlib import contextmanager, asynccontextmanager from collections.abc import Callable, Iterator, Awaitable, AsyncIterator import pytest from pydantic import BaseModel from anthropic import beta_tool from anthropic._compat import PYDANTIC_V1 from anthropic.lib.tools._beta_functions import BaseFunctionTool from anthropic.types.beta.beta_tool_param import InputSchema @pytest.mark.skipif(PYDANTIC_V1, reason="only applicable in pydantic v2") class TestFunctionTool: def test_basic_function_schema_conversion(self) -> None: """Test basic function schema conversion with simple types.""" def get_weather(location: str, unit: str = "celsius") -> str: """Get the weather for a specific location.""" return f"Weather in {location} is 20 degrees {unit}" function_tool = beta_tool(get_weather) assert function_tool.name == "get_weather" assert function_tool.description == "Get the weather for a specific location." assert function_tool.input_schema == { "additionalProperties": False, "type": "object", "properties": { "location": {"title": "Location", "type": "string"}, "unit": {"title": "Unit", "type": "string", "default": "celsius"}, }, "required": ["location"], } assert function_tool(location="CA") == "Weather in CA is 20 degrees celsius" # invalid types should be allowed because __call__ should just be the original function assert function_tool(location=cast(Any, 1)) == "Weather in 1 is 20 degrees celsius" def test_function_with_multiple_types(self) -> None: """Test function schema conversion with various Python types.""" def simple_function( name: str, age: int, ) -> str: """A simple function with basic parameter types.""" return f"Person: {name}, {age} years old" function_tool = beta_tool(simple_function) # Test that we can create the tool and call it assert function_tool.name == "simple_function" assert function_tool.description == "A simple function with basic parameter types." # Test calling the function result = function_tool.call( { "name": "John", "age": 25, } ) assert result == "Person: John, 25 years old" # Test schema structure expected_schema = { "additionalProperties": False, "type": "object", "properties": { "name": {"title": "Name", "type": "string"}, "age": {"title": "Age", "type": "integer"}, }, "required": ["name", "age"], } assert function_tool.input_schema == expected_schema def test_function_call_with_valid_input(self) -> None: def add_numbers(a: int, b: int) -> str: """Add two numbers together.""" return str(a + b) function_tool = beta_tool(add_numbers) result = function_tool.call({"a": 5, "b": 3}) assert result == "8" @pytest.mark.parametrize( "input_data, expected_error_type, expected_error_msg", [ pytest.param( {"a": "not a number", "b": 3}, ValueError, "Invalid arguments for function add_numbers", id="invalid_argument_type", ), pytest.param( {"b": 3}, ValueError, "Invalid arguments for function add_numbers", id="missing_required_argument", ), pytest.param( None, TypeError, "Input must be a dictionary, got NoneType", id="invalid_input_object", ), ], ) def test_function_call_with_invalid_input( self, input_data: dict[str, Any], expected_error_type: type[BaseException], expected_error_msg: str ) -> None: def add_numbers(a: int, b: int) -> str: return str(a + b) function_tool = beta_tool(add_numbers) with pytest.raises(expected_error_type, match=expected_error_msg): function_tool.call(input_data) def test_custom_name_and_description(self) -> None: def some_function(x: int) -> str: """Original description.""" return str(x * 2) function_tool = beta_tool(some_function, name="custom_name", description="Custom description") assert function_tool.name == "custom_name" assert function_tool.description == "Custom description" def test_custom_input_schema_with_dict(self) -> None: def some_function(x: int) -> str: return str(x * 2) custom_schema: InputSchema = { "additionalProperties": False, "type": "object", "properties": {"x": {"type": "number", "description": "A number to double"}}, "required": ["x"], } function_tool = beta_tool(some_function, input_schema=custom_schema) assert function_tool.input_schema == custom_schema def test_custom_input_schema_with_pydantic_model(self) -> None: class WeatherInput(BaseModel): location: str unit: str = "celsius" def get_weather(location: str, unit: str = "celsius") -> str: # noqa: ARG001 return f"Weather in {location}" # Pass the Pydantic model class directly as input_schema function_tool = beta_tool(get_weather, input_schema=WeatherInput) # Pydantic model schemas include additional metadata schema = function_tool.input_schema assert schema == { "title": "WeatherInput", "type": "object", "properties": { "location": {"title": "Location", "type": "string"}, "unit": {"title": "Unit", "type": "string", "default": "celsius"}, }, "required": ["location"], } def test_to_dict_method(self) -> None: def simple_func(message: str) -> str: """A simple function.""" return message function_tool = beta_tool(simple_func) tool_param = function_tool.to_dict() assert tool_param == { "name": "simple_func", "description": "A simple function.", "input_schema": { "additionalProperties": False, "type": "object", "properties": {"message": {"title": "Message", "type": "string"}}, "required": ["message"], }, } def test_function_without_docstring(self) -> None: def no_docs(x: int) -> str: # noqa: ARG001 return "" function_tool = beta_tool(no_docs) assert function_tool.description == "" def test_function_without_type_hints(self) -> None: def no_types(x, y=10): # pyright: ignore[reportUnknownParameterType, reportMissingParameterType] return x + y # pyright: ignore[reportUnknownVariableType] function_tool = beta_tool(no_types) # type: ignore # Should still create a schema, though less precise (uses Any type) assert function_tool.input_schema == { "additionalProperties": False, "type": "object", "properties": { "x": {"title": "X"}, # Any type gets title but no type "y": {"title": "Y", "default": 10}, }, "required": ["x"], } @pytest.mark.parametrize( "docstring", [ pytest.param( ( """Get detailed weather information for a location. This function retrieves current weather conditions and optionally includes a forecast for the specified location. Args: location: The city or location to get weather for. unit: Temperature unit, either 'celsius' or 'fahrenheit'. include_forecast: Whether to include forecast data. Returns: Weather information as a formatted string Examples: >>> get_weather_detailed("London") "London: 15°C, partly cloudy" >>> get_weather_detailed("New York", "fahrenheit", True) "New York: 59°F, sunny. Tomorrow: 62°F, cloudy" """ ), id="google_style_docstring", ), pytest.param( ( """Get detailed weather information for a location. This function retrieves current weather conditions and optionally includes a forecast for the specified location. :param location: The city or location to get weather for. :type location: str :param unit: Temperature unit, either 'celsius' or 'fahrenheit'. :type unit: str :param include_forecast: Whether to include forecast data. :type include_forecast: bool :returns: Weather information as a formatted string. :rtype: str :example: >>> get_weather_detailed("London") "London: 15°C, partly cloudy" >>> get_weather_detailed("New York", "fahrenheit", True) "New York: 59°F, sunny. Tomorrow: 62°F, cloudy """ ), id="rest_style_docstring", ), pytest.param( ( """Get detailed weather information for a location. This function retrieves current weather conditions and optionally includes a forecast for the specified location. Parameters ---------- location : str The city or location to get weather for. unit : str Temperature unit, either 'celsius' or 'fahrenheit'. include_forecast : bool Whether to include forecast data. Returns ------- str Weather information as a formatted string. Examples -------- >>> get_weather_detailed("London") "London: 15°C, partly cloudy" >>> get_weather_detailed("New York", "fahrenheit", True) "New York: 59°F, sunny. Tomorrow: 62°F, cloudy" """ ), id="numpy_style_docstring", ), pytest.param( ( """Get detailed weather information for a location. This function retrieves current weather conditions and optionally includes a forecast for the specified location. @param location: The city or location to get weather for. @type location: str @param unit: Temperature unit, either 'celsius' or 'fahrenheit'. @type unit: str @param include_forecast: Whether to include forecast data. @type include_forecast: bool @return: Weather information as a formatted string. @rtype: str @example: >>> get_weather_detailed("London") "London: 15°C, partly cloudy" >>> get_weather_detailed("New York", "fahrenheit", True) "New York: 59°F, sunny. Tomorrow: 62°F, cloudy" """ ), id="epydoc_style_docstring", ), ], ) def test_docstring_parsing_with_parameters(self, docstring: str) -> None: def get_weather_detailed(location: str, unit: str = "celsius", include_forecast: bool = False) -> str: # noqa: ARG001 return f"Weather for {location}" get_weather_detailed.__doc__ = docstring function_tool = beta_tool(get_weather_detailed) expected_description = ( "Get detailed weather information for a location.\n\n" "This function retrieves current weather conditions and optionally\n" "includes a forecast for the specified location." ) expected_schema = { "additionalProperties": False, "type": "object", "properties": { "location": { "title": "Location", "type": "string", "description": "The city or location to get weather for.", }, "unit": { "title": "Unit", "type": "string", "default": "celsius", "description": "Temperature unit, either 'celsius' or 'fahrenheit'.", }, "include_forecast": { "title": "Include Forecast", "type": "boolean", "default": False, "description": "Whether to include forecast data.", }, }, "required": ["location"], } assert function_tool.description == expected_description assert function_tool.input_schema == expected_schema def test_decorator_without_parentheses(self) -> None: """Test using @function_tool decorator without parentheses.""" @beta_tool def multiply(x: int, y: int) -> str: """Multiply two numbers.""" return str(x * y) assert multiply.name == "multiply" assert multiply.description == "Multiply two numbers." assert multiply.call({"x": 3, "y": 4}) == "12" expected_schema = { "additionalProperties": False, "type": "object", "properties": { "x": {"title": "X", "type": "integer"}, "y": {"title": "Y", "type": "integer"}, }, "required": ["x", "y"], } assert multiply.input_schema == expected_schema def test_decorator_with_parentheses(self) -> None: """Test using @function_tool() decorator with parentheses.""" @beta_tool() def divide(a: float, b: float) -> str: """Divide two numbers.""" return str(a / b) assert divide.name == "divide" assert divide.description == "Divide two numbers." assert divide.call({"a": 10.0, "b": 2.0}) == "5.0" def test_decorator_with_custom_parameters(self) -> None: """Test using @function_tool() decorator with custom name and description.""" @beta_tool(name="custom_calculator", description="A custom calculator function") def calculate(value: int) -> str: """Original description that should be overridden.""" return str(value * 2) assert calculate.name == "custom_calculator" assert calculate.description == "A custom calculator function" assert calculate.call({"value": 5}) == "10" def test_docstring_parsing_simple(self) -> None: """Test that simple docstrings still work correctly.""" def simple_add(a: int, b: int) -> str: """Add two numbers together.""" return str(a + b) function_tool = beta_tool(simple_add) assert function_tool.description == "Add two numbers together." assert _get_parameters_info(function_tool) == {} # Schema should not have descriptions for parameters expected_schema = { "additionalProperties": False, "type": "object", "properties": {"a": {"title": "A", "type": "integer"}, "b": {"title": "B", "type": "integer"}}, "required": ["a", "b"], } assert function_tool.input_schema == expected_schema def _get_parameters_info(fn: BaseFunctionTool[Any]) -> dict[str, str]: param_info: dict[str, str] = {} for param in fn._parsed_docstring.params: if param.description: param_info[param.arg_name] = param.description.strip() return param_info @pytest.mark.skipif(PYDANTIC_V1, reason="tool functions need pydantic v2") class TestContextManagerTool: """``@beta_tool`` / ``@beta_async_tool`` over an (async) context manager that yields the tool callable: the decorator enters it to obtain the callable and drives its ``__exit__`` / ``__aexit__`` on the cleanup path. The ``cast(Any, ...)`` call form mirrors how the SDK's own ``beta_bash_tool`` adopts this; bare decorator syntax works the same at runtime. """ def test_sync_context_manager_tool(self) -> None: import anyio from anthropic.lib.tools._beta_functions import aclose_runnable_tool seen: list[str] = [] @contextmanager def adder_cm() -> Iterator[Callable[[int, int], str]]: seen.append("enter") def add(a: int, b: int) -> str: """Add two numbers.""" return str(a + b) try: yield add finally: seen.append("exit") adder = beta_tool(cast(Any, adder_cm)) # Entered eagerly; schema/description inferred from the yielded callable. assert seen == ["enter"] assert adder.name == "add" assert adder.description == "Add two numbers." assert adder.input_schema == { "additionalProperties": False, "type": "object", "properties": {"a": {"title": "A", "type": "integer"}, "b": {"title": "B", "type": "integer"}}, "required": ["a", "b"], } assert adder.call({"a": 2, "b": 3}) == "5" anyio.run(aclose_runnable_tool, adder) assert seen == ["enter", "exit"] async def test_async_context_manager_tool_lazy_enter_and_cleanup(self) -> None: from anthropic.lib.tools._beta_functions import beta_async_tool, aclose_runnable_tool seen: list[str] = [] schema: InputSchema = { "type": "object", "properties": {"value": {"type": "string"}}, "required": ["value"], } @asynccontextmanager async def echo_cm() -> AsyncIterator[Callable[[str], Awaitable[str]]]: """Echo the value.""" seen.append("enter") async def echo(value: str) -> str: return f"echo:{value}" try: yield echo finally: seen.append("exit") echo_tool = beta_async_tool(name="echo", input_schema=schema)(cast(Any, echo_cm)) # Name/description/schema are available without entering (the runner # reads them before any tool call). assert echo_tool.name == "echo" assert echo_tool.description == "Echo the value." assert echo_tool.to_dict()["input_schema"] == schema assert seen == [] assert await echo_tool.call({"value": "hi"}) == "echo:hi" assert seen == ["enter"] await aclose_runnable_tool(echo_tool) assert seen == ["enter", "exit"] def test_wrong_decorator_raises(self) -> None: from anthropic.lib.tools._beta_functions import beta_async_tool @asynccontextmanager async def an_async_cm() -> AsyncIterator[Callable[[], str]]: yield lambda: "x" @contextmanager def a_sync_cm() -> Iterator[Callable[[], str]]: yield lambda: "x" with pytest.raises(TypeError, match="use @beta_async_tool"): beta_tool(cast(Any, an_async_cm)) with pytest.raises(TypeError, match="use @beta_tool"): beta_async_tool(name="bad2")(cast(Any, a_sync_cm)) def test_async_context_manager_requires_input_schema(self) -> None: from anthropic.lib.tools._beta_functions import beta_async_tool @asynccontextmanager async def noschema_cm() -> AsyncIterator[Callable[[int], Awaitable[str]]]: async def fn(x: int) -> str: return str(x) yield fn with pytest.raises(TypeError, match="needs an explicit input_schema"): beta_async_tool(name="noschema")(cast(Any, noschema_cm)) anthropic-sdk-python-0.120.2/tests/lib/tools/test_mcp_tool.py000066400000000000000000000371771523216435200242600ustar00rootroot00000000000000# pyright: reportUnknownArgumentType=false, reportUnknownMemberType=false, reportUnknownVariableType=false, reportMissingImports=false, reportUnknownParameterType=false from __future__ import annotations import json import base64 from typing import Any from unittest.mock import AsyncMock import anyio import pytest mcp = pytest.importorskip("mcp") from mcp.types import ( # noqa: E402 Tool, TextContent, AudioContent, ImageContent, ResourceLink, PromptMessage, CallToolResult, EmbeddedResource, ReadResourceResult, BlobResourceContents, TextResourceContents, ) from anthropic.lib.tools import ToolError from anthropic.lib.tools.mcp import ( UnsupportedMCPValueError, mcp_tool, mcp_content, mcp_message, async_mcp_tool, mcp_resource_to_file, mcp_resource_to_content, ) # ----------------------------------------------------------------------- # Helpers # ----------------------------------------------------------------------- def _text_resource(uri: str = "file:///x.txt", text: str = "hello", mime: str | None = None) -> TextResourceContents: return TextResourceContents.model_validate({"uri": uri, "text": text, **({"mimeType": mime} if mime else {})}) def _blob_resource(uri: str = "file:///x.bin", blob: str = "", mime: str | None = None) -> BlobResourceContents: return BlobResourceContents.model_validate({"uri": uri, "blob": blob, **({"mimeType": mime} if mime else {})}) def _read_result(contents: list[Any]) -> ReadResourceResult: return ReadResourceResult.model_validate({"contents": contents}) def _mock_client(result: CallToolResult | None = None) -> Any: """Return a mock that quacks like ClientSession.call_tool.""" default = CallToolResult(content=[TextContent(type="text", text="tool output")], isError=False) mock = type("MockClient", (), {"call_tool": AsyncMock(return_value=result or default)})() return mock # ----------------------------------------------------------------------- # Tests: mcp_content # ----------------------------------------------------------------------- class TestMCPContent: def test_text_content(self) -> None: result = mcp_content(TextContent(type="text", text="hello world")) assert result["type"] == "text" assert result["text"] == "hello world" def test_text_content_with_cache_control(self) -> None: result = mcp_content(TextContent(type="text", text="hi"), cache_control={"type": "ephemeral"}) assert not isinstance(result, str) assert result["type"] == "text" assert "cache_control" in result assert result["cache_control"] == {"type": "ephemeral"} def test_image_content_png(self) -> None: result = mcp_content(ImageContent(type="image", data="abc123", mimeType="image/png")) assert result["type"] == "image" source = result["source"] assert source["type"] == "base64" assert source["data"] == "abc123" assert source["media_type"] == "image/png" def test_image_content_jpeg(self) -> None: result = mcp_content(ImageContent(type="image", data="abc", mimeType="image/jpeg")) assert not isinstance(result, str) assert result["type"] == "image" assert "media_type" in result["source"] assert result["source"]["media_type"] == "image/jpeg" def test_image_content_gif(self) -> None: result = mcp_content(ImageContent(type="image", data="abc", mimeType="image/gif")) assert result["type"] == "image" def test_image_content_webp(self) -> None: result = mcp_content(ImageContent(type="image", data="abc", mimeType="image/webp")) assert result["type"] == "image" def test_image_unsupported_mime_type(self) -> None: with pytest.raises(UnsupportedMCPValueError, match="image/bmp"): mcp_content(ImageContent(type="image", data="abc", mimeType="image/bmp")) def test_embedded_resource_text(self) -> None: resource = _text_resource(text="doc content", mime="text/plain") result = mcp_content(EmbeddedResource(type="resource", resource=resource)) assert result["type"] == "document" assert result["source"]["type"] == "text" assert result["source"]["data"] == "doc content" def test_embedded_resource_pdf(self) -> None: pdf_data = base64.b64encode(b"pdf bytes").decode() resource = _blob_resource(uri="file:///doc.pdf", blob=pdf_data, mime="application/pdf") result = mcp_content(EmbeddedResource(type="resource", resource=resource)) assert result["type"] == "document" assert result["source"]["type"] == "base64" assert result["source"]["media_type"] == "application/pdf" def test_embedded_resource_image(self) -> None: resource = _blob_resource(uri="file:///img.png", blob="aW1nZGF0YQ==", mime="image/png") result = mcp_content(EmbeddedResource(type="resource", resource=resource)) assert result["type"] == "image" assert "media_type" in result["source"] assert result["source"]["media_type"] == "image/png" def test_audio_unsupported(self) -> None: with pytest.raises(UnsupportedMCPValueError, match="audio"): mcp_content(AudioContent(type="audio", data="base64data", mimeType="audio/mpeg")) def test_resource_link_unsupported(self) -> None: with pytest.raises(UnsupportedMCPValueError, match="resource_link"): mcp_content( ResourceLink.model_validate({"type": "resource_link", "uri": "https://example.com", "name": "x"}) ) def test_embedded_resource_no_mime_defaults_to_text(self) -> None: resource = _text_resource(text="content") result = mcp_content(EmbeddedResource(type="resource", resource=resource)) assert result["type"] == "document" assert result["source"]["type"] == "text" assert result["source"]["data"] == "content" def test_embedded_resource_unsupported_mime(self) -> None: resource = _blob_resource(blob="data", mime="application/octet-stream") with pytest.raises(UnsupportedMCPValueError, match="application/octet-stream"): mcp_content(EmbeddedResource(type="resource", resource=resource)) def test_embedded_resource_image_requires_blob(self) -> None: resource = _text_resource(text="not blob", mime="image/png") with pytest.raises(UnsupportedMCPValueError, match="blob data"): mcp_content(EmbeddedResource(type="resource", resource=resource)) def test_embedded_resource_pdf_requires_blob(self) -> None: resource = _text_resource(text="not blob", mime="application/pdf") with pytest.raises(UnsupportedMCPValueError, match="blob data"): mcp_content(EmbeddedResource(type="resource", resource=resource)) # ----------------------------------------------------------------------- # Tests: mcp_message # ----------------------------------------------------------------------- class TestMCPMessage: def test_user_message(self) -> None: msg = PromptMessage(role="user", content=TextContent(type="text", text="hello")) result = mcp_message(msg) assert result["role"] == "user" content_list = result["content"] assert len(content_list) == 1 block: Any = content_list[0] assert block["type"] == "text" assert block["text"] == "hello" def test_assistant_message(self) -> None: msg = PromptMessage(role="assistant", content=TextContent(type="text", text="hi there")) result = mcp_message(msg) assert result["role"] == "assistant" def test_message_with_cache_control(self) -> None: msg = PromptMessage(role="user", content=TextContent(type="text", text="hi")) result = mcp_message(msg, cache_control={"type": "ephemeral"}) block = result["content"][0] assert block["cache_control"] == {"type": "ephemeral"} def test_list_comprehension(self) -> None: msgs = [ PromptMessage(role="user", content=TextContent(type="text", text="q1")), PromptMessage(role="assistant", content=TextContent(type="text", text="a1")), ] result = [mcp_message(m) for m in msgs] assert len(result) == 2 assert result[0]["role"] == "user" assert result[1]["role"] == "assistant" # ----------------------------------------------------------------------- # Tests: mcp_resource_to_content # ----------------------------------------------------------------------- class TestMCPResourceToContent: def test_text_resource(self) -> None: result = mcp_resource_to_content(_read_result([_text_resource(text="hello", mime="text/plain").model_dump()])) assert result["type"] == "document" assert "data" in result["source"] assert result["source"]["data"] == "hello" def test_pdf_resource(self) -> None: pdf_data = base64.b64encode(b"pdf content").decode() result = mcp_resource_to_content( _read_result([_blob_resource(blob=pdf_data, mime="application/pdf").model_dump()]) ) assert result["type"] == "document" assert "media_type" in result["source"] assert result["source"]["media_type"] == "application/pdf" def test_image_resource(self) -> None: result = mcp_resource_to_content(_read_result([_blob_resource(blob="aW1n", mime="image/png").model_dump()])) assert result["type"] == "image" def test_empty_contents_raises(self) -> None: with pytest.raises(UnsupportedMCPValueError, match="at least one item"): mcp_resource_to_content(ReadResourceResult(contents=[])) def test_no_supported_mime_raises(self) -> None: with pytest.raises(UnsupportedMCPValueError, match="No supported MIME type"): mcp_resource_to_content( _read_result([_blob_resource(blob="", mime="application/octet-stream").model_dump()]) ) def test_selects_first_supported(self) -> None: result = mcp_resource_to_content( _read_result( [ _blob_resource(blob="", mime="application/octet-stream").model_dump(), _text_resource(text="found it", mime="text/plain").model_dump(), ] ) ) assert result["type"] == "document" assert "data" in result["source"] assert result["source"]["data"] == "found it" # ----------------------------------------------------------------------- # Tests: mcp_resource_to_file # ----------------------------------------------------------------------- class TestMCPResourceToFile: def test_text_resource(self) -> None: name, data, _ = mcp_resource_to_file( _read_result([_text_resource(uri="file:///path/to/doc.txt", text="hello").model_dump()]) ) assert name == "doc.txt" assert data == b"hello" def test_blob_resource(self) -> None: blob = base64.b64encode(b"binary data").decode() name, data, mime = mcp_resource_to_file( _read_result([_blob_resource(uri="file:///img.png", blob=blob, mime="image/png").model_dump()]) ) assert name == "img.png" assert data == b"binary data" assert mime == "image/png" def test_empty_contents_raises(self) -> None: with pytest.raises(UnsupportedMCPValueError): mcp_resource_to_file(ReadResourceResult(contents=[])) # ----------------------------------------------------------------------- # Tests: tool wrappers # ----------------------------------------------------------------------- class TestMCPToolFactory: def test_mcp_tool_to_dict(self) -> None: tool = Tool( name="my_tool", description="Does stuff", inputSchema={"type": "object", "properties": {"x": {"type": "integer"}}}, ) d: Any = mcp_tool(tool, _mock_client()).to_dict() assert d["name"] == "my_tool" assert d["description"] == "Does stuff" assert d["input_schema"]["type"] == "object" def test_mcp_tool_name(self) -> None: tool = Tool(name="my_tool", inputSchema={"type": "object"}) runnable = mcp_tool(tool, _mock_client()) assert runnable.name == "my_tool" def test_mcp_tool_no_description(self) -> None: tool = Tool(name="t", inputSchema={"type": "object"}) d: Any = mcp_tool(tool, _mock_client()).to_dict() assert d.get("description", "") == "" def test_mcp_tool_with_cache_control(self) -> None: tool = Tool(name="t", inputSchema={"type": "object"}) d: Any = mcp_tool(tool, _mock_client(), cache_control={"type": "ephemeral"}).to_dict() assert d["cache_control"] == {"type": "ephemeral"} def test_list_comprehension(self) -> None: tools = [Tool(name="t1", inputSchema={"type": "object"}), Tool(name="t2", inputSchema={"type": "object"})] client = _mock_client() result = [mcp_tool(t, client) for t in tools] assert len(result) == 2 assert result[0].name == "t1" assert result[1].name == "t2" class TestAsyncMCPToolFactory: def test_async_mcp_tool_to_dict(self) -> None: tool = Tool(name="async_tool", inputSchema={"type": "object"}) d: Any = async_mcp_tool(tool, _mock_client()).to_dict() assert d["name"] == "async_tool" class TestAsyncMCPToolCall: def test_call_success(self) -> None: async def _test() -> None: tool = Tool(name="calc", inputSchema={"type": "object"}) call_result = CallToolResult(content=[TextContent(type="text", text="42")], isError=False) client = _mock_client(result=call_result) runnable = async_mcp_tool(tool, client) result = await runnable.call({"x": 1}) assert isinstance(result, list) block = result[0] assert block["type"] == "text" assert block["text"] == "42" client.call_tool.assert_awaited_once_with(name="calc", arguments={"x": 1}) anyio.run(_test) def test_call_error(self) -> None: async def _test() -> None: tool = Tool(name="fail_tool", inputSchema={"type": "object"}) call_result = CallToolResult( content=[TextContent(type="text", text="something went wrong")], isError=True, ) client = _mock_client(result=call_result) runnable = async_mcp_tool(tool, client) with pytest.raises(ToolError, match="something went wrong") as exc_info: await runnable.call({}) # ToolError carries structured content blocks content = list(exc_info.value.content) block: Any = content[0] assert block["type"] == "text" assert block["text"] == "something went wrong" anyio.run(_test) def test_call_structured_content_fallback(self) -> None: async def _test() -> None: tool = Tool(name="structured", inputSchema={"type": "object"}) call_result = CallToolResult(content=[], structuredContent={"key": "value"}, isError=False) client = _mock_client(result=call_result) runnable = async_mcp_tool(tool, client) result = await runnable.call({}) assert result == json.dumps({"key": "value"}) anyio.run(_test) def test_call_empty_content_returns_empty_list(self) -> None: async def _test() -> None: tool = Tool(name="empty", inputSchema={"type": "object"}) call_result = CallToolResult(content=[], isError=False) client = _mock_client(result=call_result) runnable = async_mcp_tool(tool, client) result = await runnable.call({}) assert result == [] anyio.run(_test) anthropic-sdk-python-0.120.2/tests/lib/tools/test_runners.py000066400000000000000000001473541523216435200241370ustar00rootroot00000000000000import os import json import logging from typing import Any, Dict, List, Union, cast from typing_extensions import Literal import httpx import pytest from respx import MockRouter from inline_snapshot import external, snapshot from anthropic import Anthropic, AsyncAnthropic, beta_tool, beta_async_tool from anthropic._utils import assert_signatures_in_sync from anthropic._compat import PYDANTIC_V1 from anthropic.lib.tools import BetaFunctionToolResultType from anthropic.lib.tools._tool_dispatch import available_tool_names from anthropic.types.beta.beta_message_param import BetaMessageParam from anthropic.types.beta.beta_content_block_param import BetaContentBlockParam from anthropic.types.beta.beta_tool_result_block_param import BetaToolResultBlockParam from anthropic.types.beta.beta_tool_change_tool_reference_param import BetaToolChangeToolReferenceParam from ..utils import print_obj base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") # all the snapshots in this file are auto-generated from the live API # # you can update them with # # `ANTHROPIC_LIVE=1 ./scripts/test --inline-snapshot=fix -n0` snapshots = { "basic": { "responses": snapshot( [ '{"model": "claude-haiku-4-5-20251001", "id": "msg_0133AjAuLSKXatUZqNkpALPx", "type": "message", "role": "assistant", "content": [{"type": "tool_use", "id": "toolu_01DGiQScbZKPwUBYN79rFUb8", "name": "get_weather", "input": {"location": "San Francisco, CA", "units": "f"}}], "stop_reason": "tool_use", "stop_sequence": null, "usage": {"input_tokens": 656, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, "output_tokens": 74, "service_tier": "standard"}}', '{"model": "claude-haiku-4-5-20251001", "id": "msg_014x2Sxq2p6sewFyUbJp8Mg3", "type": "message", "role": "assistant", "content": [{"type": "text", "text": "The weather in San Francisco, CA is currently **68\\u00b0F** and **Sunny**. It\'s a nice day! \\u2600\\ufe0f"}], "stop_reason": "end_turn", "stop_sequence": null, "usage": {"input_tokens": 770, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, "output_tokens": 33, "service_tier": "standard"}}', ] ), "result": snapshot( """\ ParsedBetaMessage( container=None, content=[ ParsedBetaTextBlock( citations=None, parsed_output=None, text='The weather in San Francisco, CA is currently **Sunny** with a temperature of **68°F**.', type='text' ) ], context_management=None, diagnostics=None, id='msg_01BZsMQjer9AFLgmdRKJ8NcA', model='claude-haiku-4-5-20251001', role='assistant', stop_details=None, stop_reason='end_turn', stop_sequence=None, type='message', usage=BetaUsage( cache_creation=BetaCacheCreation(ephemeral_1h_input_tokens=0, ephemeral_5m_input_tokens=0), cache_creation_input_tokens=0, cache_read_input_tokens=0, fallback_credit=None, inference_geo='not_available', input_tokens=770, iterations=None, output_tokens=25, output_tokens_details=None, server_tool_use=None, service_tier='standard', speed=None ) ) """ ), }, "custom": { "responses": snapshot( [ '{"model": "claude-haiku-4-5-20251001", "id": "msg_01FKEKbzbqHmJv5ozwH7tz99", "type": "message", "role": "assistant", "content": [{"type": "text", "text": "Let me check the weather for San Francisco for you in Celsius."}, {"type": "tool_use", "id": "toolu_01MxFFv4azdWzubHT3dXurMY", "name": "get_weather", "input": {"location": "San Francisco, CA", "units": "c"}}], "stop_reason": "tool_use", "stop_sequence": null, "usage": {"input_tokens": 659, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, "output_tokens": 88, "service_tier": "standard"}}', '{"model": "claude-haiku-4-5-20251001", "id": "msg_01DSPL7PHKQYTe9VAFkHzsA3", "type": "message", "role": "assistant", "content": [{"type": "text", "text": "The weather in San Francisco, CA is currently **20\\u00b0C** and **Sunny**. Nice weather!"}], "stop_reason": "end_turn", "stop_sequence": null, "usage": {"input_tokens": 787, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, "output_tokens": 26, "service_tier": "standard"}}', ] ), "result": snapshot( "ParsedBetaMessage(container=None, content=[ParsedBetaTextBlock(citations=None, parsed_output=None, text='The weather in San Francisco, CA is currently **20°C** and **Sunny**. Nice weather!', type='text')], context_management=None, id='msg_01DSPL7PHKQYTe9VAFkHzsA3', model='claude-haiku-4-5-20251001', role='assistant', stop_details=None, stop_reason='end_turn', stop_sequence=None, type='message', usage=BetaUsage(cache_creation=BetaCacheCreation(ephemeral_1h_input_tokens=0, ephemeral_5m_input_tokens=0), cache_creation_input_tokens=0, cache_read_input_tokens=0, inference_geo=None, input_tokens=787, iterations=None, output_tokens=26, server_tool_use=None, service_tier='standard', speed=None))\n" ), }, "streaming": { "result": snapshot( """\ ParsedBetaMessage( container=None, content=[ ParsedBetaTextBlock( citations=None, parsed_output=None, text="The weather in San Francisco, CA is currently **68°F and Sunny**. It's a nice day!", type='text' ) ], context_management=None, diagnostics=None, id='msg_0158JyopQTFaomteeJoDpS5q', model='claude-haiku-4-5-20251001', role='assistant', stop_details=None, stop_reason='end_turn', stop_sequence=None, type='message', usage=BetaUsage( cache_creation=BetaCacheCreation(ephemeral_1h_input_tokens=0, ephemeral_5m_input_tokens=0), cache_creation_input_tokens=0, cache_read_input_tokens=0, fallback_credit=None, inference_geo='not_available', input_tokens=770, iterations=None, output_tokens=27, output_tokens_details=None, server_tool_use=None, service_tier='standard', speed=None ) ) """ ) }, "tool_call": { "responses": snapshot( [ '{"model": "claude-haiku-4-5-20251001", "id": "msg_01NzLkujbJ7VQgzNHFx76Ab4", "type": "message", "role": "assistant", "content": [{"type": "tool_use", "id": "toolu_01SPe52JjANtJDVJ5yUZj4jz", "name": "get_weather", "input": {"location": "SF", "units": "c"}}], "stop_reason": "tool_use", "stop_sequence": null, "usage": {"input_tokens": 597, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, "output_tokens": 71, "service_tier": "standard"}}', '{"model": "claude-haiku-4-5-20251001", "id": "msg_016bjf5SAczxp28ES4yX7Z7U", "type": "message", "role": "assistant", "content": [{"type": "text", "text": "The weather in SF (San Francisco) is currently **20\\u00b0C** and **sunny**!"}], "stop_reason": "end_turn", "stop_sequence": null, "usage": {"input_tokens": 705, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, "output_tokens": 23, "service_tier": "standard"}}', ] ), }, "tool_call_error": { "responses": snapshot( [ '{"model": "claude-haiku-4-5-20251001", "id": "msg_01QhmJFoA3mxD2mxPFnjLHrT", "type": "message", "role": "assistant", "content": [{"type": "tool_use", "id": "toolu_01Do4cDVNxt51EuosKoxdmii", "name": "get_weather", "input": {"location": "San Francisco, CA", "units": "f"}}], "stop_reason": "tool_use", "stop_sequence": null, "usage": {"input_tokens": 656, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, "output_tokens": 74, "service_tier": "standard"}}', '{"model": "claude-haiku-4-5-20251001", "id": "msg_0137FupJYD4A3Mc6jUUxKpU6", "type": "message", "role": "assistant", "content": [{"type": "text", "text": "I apologize, but I encountered an error when trying to fetch the weather for San Francisco. This appears to be a temporary issue with the weather service. Could you please try again in a moment, or let me know if you\'d like me to attempt the lookup again?"}], "stop_reason": "end_turn", "stop_sequence": null, "usage": {"input_tokens": 760, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 0, "cache_creation": {"ephemeral_5m_input_tokens": 0, "ephemeral_1h_input_tokens": 0}, "output_tokens": 58, "service_tier": "standard"}}', ] ) }, } @pytest.mark.skipif(PYDANTIC_V1, reason="tool runner not supported with pydantic v1") class TestSyncRunTools: @pytest.mark.parametrize( "http_snapshot", [ cast(Any, external("uuid:b38bbf6c-9a76-40ca-b09d-7a3911776e0f.json")), ], ) def test_basic_call_sync(self, snapshot_client: Anthropic) -> None: @beta_tool def get_weather(location: str, units: Literal["c", "f"]) -> BetaFunctionToolResultType: """Lookup the weather for a given city in either celsius or fahrenheit Args: location: The city and state, e.g. San Francisco, CA units: Unit for the output, either 'c' for celsius or 'f' for fahrenheit Returns: A dictionary containing the location, temperature, and weather condition. """ return json.dumps(_get_weather(location, units)) message = snapshot_client.beta.messages.tool_runner( max_tokens=1024, model="claude-haiku-4-5", tools=[get_weather], messages=[{"role": "user", "content": "What is the weather in SF?"}], ).until_done() assert print_obj(message) == snapshots["basic"]["result"] @pytest.mark.parametrize( "http_snapshot", [ cast(Any, external("uuid:10e53c1d-51be-4c64-b5bf-99adb3fa4719.json")), ], ) def test_tool_call_error( self, snapshot_client: Anthropic, caplog: pytest.LogCaptureFixture, ) -> None: called = None @beta_tool def get_weather(location: str, units: Literal["c", "f"]) -> BetaFunctionToolResultType: """Lookup the weather for a given city in either celsius or fahrenheit Args: location: The city and state, e.g. San Francisco, CA units: Unit for the output, either 'c' for celsius or 'f' for fahrenheit Returns: A dictionary containing the location, temperature, and weather condition. """ nonlocal called if called is None: called = True raise RuntimeError("Unexpected error, try again") return json.dumps(_get_weather(location, units)) runner = snapshot_client.beta.messages.tool_runner( max_tokens=1024, model="claude-haiku-4-5", tools=[get_weather], messages=[{"role": "user", "content": "What is the weather in SF?"}], ) actual_responses: List[Union[BetaMessageParam, None]] = [] with caplog.at_level(logging.ERROR): for _ in runner: tool_call_response = runner.generate_tool_call_response() if tool_call_response is not None: actual_responses.append(tool_call_response) message = actual_responses assert caplog.record_tuples == [ ( "anthropic.lib.tools._beta_runner", logging.ERROR, "Error occurred while calling tool: get_weather", ), ] assert print_obj(message) == snapshot( """\ [ { 'role': 'user', 'content': [ { 'type': 'tool_result', 'tool_use_id': 'toolu_01A9HHF5Ezy3oBrKmSgfASm9', 'content': "RuntimeError('Unexpected error, try again')", 'is_error': True } ] } ] """ ) @pytest.mark.parametrize( "http_snapshot", [ cast(Any, external("uuid:f59a9391-643b-422c-96dc-1f28bc7ea4d7.json")), ], ) # TODO: fix the append_messages method @pytest.mark.xfail(reason="bug in append messages") def test_custom_message_handling(self, snapshot_client: Anthropic) -> None: @beta_tool def get_weather(location: str, units: Literal["c", "f"]) -> BetaFunctionToolResultType: """Lookup the weather for a given city in either celsius or fahrenheit Args: location: The city and state, e.g. San Francisco, CA units: Unit for the output, either 'c' for celsius or 'f' for fahrenheit Returns: A dictionary containing the location, temperature, and weather condition. """ return json.dumps(_get_weather(location, units)) runner = snapshot_client.beta.messages.tool_runner( model="claude-haiku-4-5", messages=[{"role": "user", "content": "What's the weather in SF in Celsius?"}], tools=[get_weather], max_tokens=1024, ) for message_iter in runner: if message_iter.content[0].type == "tool_use": runner.append_messages( BetaMessageParam( role="user", content=[ BetaToolResultBlockParam( tool_use_id=message_iter.content[0].id, content="The weather in San Francisco, CA is currently sunny with a temperature of 20°C.", type="tool_result", ) ], ), ) message = runner.until_done() assert print_obj(message) == snapshots["custom"]["result"] @pytest.mark.parametrize( "http_snapshot", [ cast(Any, external("uuid:a8ac789b-f856-48cd-9ff3-d5f36799e432.json")), ], ) def test_tool_call_caching(self, snapshot_client: Anthropic) -> None: called = None @beta_tool def get_weather(location: str, units: Literal["c", "f"]) -> BetaFunctionToolResultType: nonlocal called """Lookup the weather for a given city in either celsius or fahrenheit Args: location: The city and state, e.g. San Francisco, CA units: Unit for the output, either 'c' for celsius or 'f' for fahrenheit Returns: A dictionary containing the location, temperature, and weather condition. """ if called is None: called = True return json.dumps(_get_weather(location, units)) raise RuntimeError("This tool should not be called again") runner = snapshot_client.beta.messages.tool_runner( model="claude-haiku-4-5", messages=[{"role": "user", "content": "What's the weather in SF in Celsius?"}], tools=[get_weather], max_tokens=1024, ) for _ in runner: response1 = runner.generate_tool_call_response() response2 = runner.generate_tool_call_response() if response1 is not None: assert response1 is response2 @pytest.mark.parametrize( "http_snapshot", [ cast(Any, external("uuid:771c64ff-a0af-4cd9-8080-a5a539da7cb9.json")), ], ) def test_streaming_call_sync(self, snapshot_client: Anthropic) -> None: @beta_tool def get_weather(location: str, units: Literal["c", "f"]) -> BetaFunctionToolResultType: """Lookup the weather for a given city in either celsius or fahrenheit Args: location: The city and state, e.g. San Francisco, CA units: Unit for the output, either 'c' for celsius or 'f' for fahrenheit Returns: A dictionary containing the location, temperature, and weather condition. """ return json.dumps(_get_weather(location, units)) last_response_messsage = snapshot_client.beta.messages.tool_runner( max_tokens=1024, model="claude-haiku-4-5", tools=[get_weather], messages=[{"role": "user", "content": "What is the weather in SF?"}], stream=True, ).until_done() assert print_obj(last_response_messsage) == snapshots["streaming"]["result"] @pytest.mark.parametrize( "http_snapshot", [ cast(Any, external("uuid:e075a6c2-de4d-4125-9709-f0e178058190.json")), ], ) def test_max_iterations(self, snapshot_client: Anthropic) -> None: @beta_tool def get_weather(location: str, units: Literal["c", "f"]) -> BetaFunctionToolResultType: """Lookup the weather for a given city in either celsius or fahrenheit Args: location: The city and state, e.g. San Francisco, CA units: Unit for the output, either 'c' for celsius or 'f' for fahrenheit Returns: A dictionary containing the location, temperature, and weather condition. """ return json.dumps(_get_weather(location, units)) runner = snapshot_client.beta.messages.tool_runner( max_tokens=1024, model="claude-haiku-4-5", tools=[get_weather], messages=[ { "role": "user", "content": ( "What's the weather in San Francisco, New York, London, Tokyo and Paris?" "If you need to use tools, call only one tool at a time. Wait for the tool's" "response before making another call. Never call multiple tools at once." ), } ], max_iterations=2, ) answers: List[Union[BetaMessageParam, None]] = [] for _ in runner: answers.append(runner.generate_tool_call_response()) assert print_obj(answers) == snapshot( """\ [ { 'role': 'user', 'content': [ { 'type': 'tool_result', 'tool_use_id': 'toolu_01LRanfq6DmHn1yDTB4d1SAh', 'content': '{"location": "San Francisco, CA", "temperature": "68\\\\u00b0F", "condition": "Sunny"}' } ] }, { 'role': 'user', 'content': [ { 'type': 'tool_result', 'tool_use_id': 'toolu_01RWdcDdE8NAFDgZ8F9Xk2K7', 'content': '{"location": "New York, NY", "temperature": "68\\\\u00b0F", "condition": "Sunny"}' } ] } ] """ ) @pytest.mark.parametrize( "http_snapshot", [ cast(Any, external("uuid:555fb399-a54c-455b-9ac5-2c9673f18e12.json")), ], ) def test_streaming_call_sync_events(self, snapshot_client: Anthropic) -> None: @beta_tool def get_weather(location: str, units: Literal["c", "f"]) -> BetaFunctionToolResultType: """Lookup the weather for a given city in either celsius or fahrenheit Args: location: The city and state, e.g. San Francisco, CA units: Unit for the output, either 'c' for celsius or 'f' for fahrenheit Returns: A dictionary containing the location, temperature, and weather condition. """ return json.dumps(_get_weather(location, units)) events: list[str] = [] runner = snapshot_client.beta.messages.tool_runner( max_tokens=1024, model="claude-haiku-4-5", tools=[get_weather], messages=[{"role": "user", "content": "What is the weather in SF?"}], stream=True, ) for stream in runner: for event in stream: events.append(event.type) assert set(events) == snapshot( { "content_block_delta", "content_block_start", "content_block_stop", "input_json", "message_delta", "message_start", "message_stop", "text", } ) @pytest.mark.parametrize( "http_snapshot", [ cast(Any, external("uuid:956fa2fe-8752-4f7c-8f9a-33735e62b898.json")), ], ) def test_compaction_control(self, snapshot_client: Anthropic, caplog: pytest.LogCaptureFixture) -> None: @beta_tool def submit_analysis(summary: str) -> str: # noqa: ARG001 """Call this LAST with your final analysis.""" return "Analysis submitted" with pytest.warns(DeprecationWarning, match="compaction_control.*deprecated"): runner = snapshot_client.beta.messages.tool_runner( model="claude-sonnet-4-5", max_tokens=4000, tools=[submit_analysis], messages=[ { "role": "user", "content": ( "Write a detailed 500 word essay about dogs, cats, and birds. " "Call the tool submit_analysis with the information about all three animals. " "Note that you should call it only once at the end of your essay." ), } ], betas=["structured-outputs-2025-12-15"], compaction_control={"enabled": True, "context_token_threshold": 500}, max_iterations=1, ) with caplog.at_level(logging.INFO, logger="anthropic.lib.tools._beta_runner"): next(runner) runner.until_done() messages = list(runner._params["messages"]) assert len(messages) == 1 assert messages[0]["role"] == "user" content = list(messages[0]["content"])[0] assert isinstance(content, dict) assert content["type"] == "text" assert content["text"] == snapshot("""\ ## Task Overview The user requests a detailed 500-word essay about dogs, cats, and birds, followed by a single call to the `submit_analysis` tool at the end containing information about all three animals. \n\ **Key Requirements:** - Essay must be 500 words in length - Cover dogs, cats, and birds - Call `submit_analysis` tool only once at the completion - The tool call should contain information about all three animals ## Current State **Status:** Not started - no work has been completed yet. **Completed:** - None **Artifacts Produced:** - None ## Important Discoveries **Unknown Information:** - The exact structure/parameters expected by the `submit_analysis` tool (need to determine what format the tool accepts) - Whether the tool requires specific data fields for each animal or free-form text - The level of detail expected in the analysis (scientific, casual, comparative, etc.) **Assumptions to Verify:** - The essay should likely compare/contrast the three animals as pets or discuss their characteristics - The `submit_analysis` tool probably accepts structured data about the animals ## Next Steps 1. **Write the 500-word essay** covering: - Dogs (characteristics, behavior, role as pets) - Cats (characteristics, behavior, role as pets) - Birds (characteristics, behavior, role as pets) - Potentially comparative elements between the three 2. **Determine the `submit_analysis` tool structure** - check what parameters it accepts 3. **Call `submit_analysis` once** with comprehensive information about all three animals in the appropriate format 4. **Verify word count** is approximately 500 words before submitting ## Context to Preserve - User emphasized calling the tool "only once at the end" - this is a specific constraint to respect - The tool should contain information about "all three animals" - comprehensive coverage required - Essay should be "detailed" - suggests substantive content rather than superficial treatment ## Priority High priority on understanding the `submit_analysis` tool parameters before writing the essay, as the content may need to be structured to align with tool requirements. \ """) assert caplog.record_tuples == snapshot( [ ( "anthropic.lib.tools._beta_runner", 20, "Token usage 1612 has exceeded the threshold of 500. Performing compaction.", ), ("anthropic.lib.tools._beta_runner", 20, "Compaction complete. New token usage: 486"), ] ) @pytest.mark.parametrize("snapshot_client", [False], indirect=True) @pytest.mark.parametrize( "http_snapshot", [ cast(Any, external("uuid:32da0815-2270-4d29-87be-3b5b63ab42e2.json")), ], ) def test_server_side_tool( self, snapshot_client: Anthropic, ) -> None: runner = snapshot_client.beta.messages.tool_runner( model="claude-haiku-4-5", messages=[{"role": "user", "content": "What is the weather in SF?"}], tools=[ { "type": "web_search_20250305", "name": "web_search", } ], max_tokens=1024, ) message = next(runner) content_types = [content.type for content in message.content] assert "server_tool_use" in content_types assert "web_search_tool_result" in content_types @pytest.mark.parametrize( "http_snapshot", [ cast(Any, external("uuid:092be1de-d3f8-4c22-a4ea-a7ad54689836.json")), ], ) def test_programmatic_tool_call(self, snapshot_client: Anthropic) -> None: @beta_tool(allowed_callers=["code_execution_20260120"]) def get_weather(location: str, units: Literal["c", "f"]) -> BetaFunctionToolResultType: """Lookup the weather for a given city in either celsius or fahrenheit Args: location: The city and state, e.g. San Francisco, CA units: Unit for the output, either 'c' for celsius or 'f' for fahrenheit Returns: A dictionary containing the location, temperature, and weather condition. """ return json.dumps(_get_weather(location, units)) runner = snapshot_client.beta.messages.tool_runner( max_tokens=1024, model="claude-opus-4-5", tools=[get_weather], messages=[{"role": "user", "content": "What is the weather in SF, NY, and London in Celsius?"}], ) first_response = next(runner) # one more iteration so runner can process the tool call response and update its params with the container info next(runner) assert first_response.container is not None container_id = first_response.container.id assert "container" in runner._params assert runner._params["container"] is not None assert container_id == runner._params["container"] @pytest.mark.skipif(PYDANTIC_V1, reason="tool runner not supported with pydantic v1") @pytest.mark.parametrize( "http_snapshot", [ cast(Any, external("uuid:64fe7974-681a-4023-9848-b32ba39c8664.json")), ], ) async def test_basic_call_async(async_snapshot_client: AsyncAnthropic) -> None: @beta_async_tool async def get_weather(location: str, units: Literal["c", "f"]) -> BetaFunctionToolResultType: """Lookup the weather for a given city in either celsius or fahrenheit Args: location: The city and state, e.g. San Francisco, CA units: Unit for the output, either 'c' for celsius or 'f' for fahrenheit Returns: A dictionary containing the location, temperature, and weather condition. """ return json.dumps(_get_weather(location, units)) await async_snapshot_client.beta.messages.tool_runner( max_tokens=1024, model="claude-haiku-4-5", tools=[get_weather], messages=[{"role": "user", "content": "What is the weather in SF?"}], ).until_done() def _refusal_with_tool_use() -> httpx.Response: return httpx.Response( 200, json={ "id": "msg_refusal", "type": "message", "role": "assistant", "model": "claude-haiku-4-5", "content": [ { "type": "tool_use", "id": "toolu_refusal", "name": "get_weather", "input": {"location": "San Francisco, CA", "units": "f"}, } ], "stop_reason": "refusal", "stop_sequence": None, "usage": {"input_tokens": 1, "output_tokens": 1}, }, ) @pytest.mark.skipif(PYDANTIC_V1, reason="tool runner not supported with pydantic v1") @pytest.mark.respx(base_url=base_url) def test_refusal_ends_runner_without_executing_tools_sync(respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[_refusal_with_tool_use()]) called = False @beta_tool def get_weather(location: str, units: Literal["c", "f"]) -> BetaFunctionToolResultType: """Lookup the weather for a given city in either celsius or fahrenheit Args: location: The city and state, e.g. San Francisco, CA units: Unit for the output, either 'c' for celsius or 'f' for fahrenheit Returns: A dictionary containing the location, temperature, and weather condition. """ nonlocal called called = True return json.dumps(_get_weather(location, units)) with Anthropic( base_url=base_url, api_key="my-anthropic-api-key", _strict_response_validation=True, max_retries=0 ) as client: runner = client.beta.messages.tool_runner( max_tokens=1024, model="claude-haiku-4-5", tools=[get_weather], messages=[{"role": "user", "content": "What is the weather in SF?"}], ) message = runner.until_done() assert message.stop_reason == "refusal" assert called is False assert len(respx_mock.calls) == 1 @pytest.mark.skipif(PYDANTIC_V1, reason="tool runner not supported with pydantic v1") @pytest.mark.respx(base_url=base_url) async def test_refusal_ends_runner_without_executing_tools_async(respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(side_effect=[_refusal_with_tool_use()]) called = False @beta_async_tool async def get_weather(location: str, units: Literal["c", "f"]) -> BetaFunctionToolResultType: """Lookup the weather for a given city in either celsius or fahrenheit Args: location: The city and state, e.g. San Francisco, CA units: Unit for the output, either 'c' for celsius or 'f' for fahrenheit Returns: A dictionary containing the location, temperature, and weather condition. """ nonlocal called called = True return json.dumps(_get_weather(location, units)) async with AsyncAnthropic( base_url=base_url, api_key="my-anthropic-api-key", _strict_response_validation=True, max_retries=0 ) as client: runner = client.beta.messages.tool_runner( max_tokens=1024, model="claude-haiku-4-5", tools=[get_weather], messages=[{"role": "user", "content": "What is the weather in SF?"}], ) message = await runner.until_done() assert message.stop_reason == "refusal" assert called is False assert len(respx_mock.calls) == 1 def _tool_use_response(tool_name: str, tool_use_id: str, input: Union[Dict[str, Any], None] = None) -> httpx.Response: return httpx.Response( 200, json={ "id": f"msg_{tool_use_id}", "type": "message", "role": "assistant", "model": "claude-haiku-4-5", "content": [ { "type": "tool_use", "id": tool_use_id, "name": tool_name, "input": input if input is not None else {"location": "San Francisco, CA", "units": "f"}, } ], "stop_reason": "tool_use", "stop_sequence": None, "usage": {"input_tokens": 1, "output_tokens": 1}, }, ) def _end_turn_response() -> httpx.Response: return httpx.Response( 200, json={ "id": "msg_end_turn", "type": "message", "role": "assistant", "model": "claude-haiku-4-5", "content": [{"type": "text", "text": "Done."}], "stop_reason": "end_turn", "stop_sequence": None, "usage": {"input_tokens": 1, "output_tokens": 1}, }, ) def _tool_reference_block(kind: Literal["tool_removal", "tool_addition"], name: str) -> BetaContentBlockParam: tool: BetaToolChangeToolReferenceParam = {"type": "tool_reference", "name": name} if kind == "tool_removal": return {"type": "tool_removal", "tool": tool} return {"type": "tool_addition", "tool": tool} def _run_sync_tool_use( client: Anthropic, *, tools: List[Any], messages: List[BetaMessageParam], ) -> List[BetaMessageParam]: """Drive a tool runner over ``messages`` and collect the tool_result messages it generated.""" runner = client.beta.messages.tool_runner( max_tokens=1024, model="claude-haiku-4-5", tools=tools, messages=messages, ) responses: List[BetaMessageParam] = [] for _ in runner: response = runner.generate_tool_call_response() if response is not None: responses.append(response) return responses @pytest.mark.skipif(PYDANTIC_V1, reason="tool runner not supported with pydantic v1") @pytest.mark.respx(base_url=base_url) def test_tool_removal_routes_call_down_unknown_tool_path_sync(respx_mock: MockRouter) -> None: # First runner: `get_weather` is registered but withdrawn mid-conversation via `tool_removal`. # Second runner: `get_weather` was never declared at all. The model calls it in both; # the resulting tool_result must be identical. respx_mock.post("/v1/messages").mock( side_effect=[ _tool_use_response("get_weather", "toolu_change"), _end_turn_response(), _tool_use_response("get_weather", "toolu_change"), _end_turn_response(), ] ) weather_called = False @beta_tool def get_weather(location: str, units: Literal["c", "f"]) -> BetaFunctionToolResultType: """Lookup the weather for a given city.""" nonlocal weather_called weather_called = True return json.dumps(_get_weather(location, units)) @beta_tool def get_time(timezone: str) -> BetaFunctionToolResultType: """Lookup the current time in a timezone.""" return timezone with Anthropic( base_url=base_url, api_key="my-anthropic-api-key", _strict_response_validation=True, max_retries=0 ) as client: with pytest.warns(UserWarning, match="Tool 'get_weather' not found in tool runner"): removed_results = _run_sync_tool_use( client, tools=[get_weather], messages=[ {"role": "user", "content": "What is the weather in SF?"}, {"role": "system", "content": [_tool_reference_block("tool_removal", "get_weather")]}, ], ) with pytest.warns(UserWarning, match="Tool 'get_weather' not found in tool runner"): never_defined_results = _run_sync_tool_use( client, tools=[get_time], messages=[{"role": "user", "content": "What is the weather in SF?"}], ) assert weather_called is False assert ( removed_results == never_defined_results == [ { "role": "user", "content": [ { "type": "tool_result", "tool_use_id": "toolu_change", "content": "Error: Tool 'get_weather' not found", "is_error": True, } ], } ] ) @pytest.mark.skipif(PYDANTIC_V1, reason="tool runner not supported with pydantic v1") @pytest.mark.respx(base_url=base_url) def test_tool_addition_re_enables_removed_tool_sync(respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( side_effect=[ _tool_use_response("get_weather", "toolu_change"), _end_turn_response(), ] ) weather_called = False @beta_tool def get_weather(location: str, units: Literal["c", "f"]) -> BetaFunctionToolResultType: """Lookup the weather for a given city.""" nonlocal weather_called weather_called = True return json.dumps(_get_weather(location, units)) with Anthropic( base_url=base_url, api_key="my-anthropic-api-key", _strict_response_validation=True, max_retries=0 ) as client: results = _run_sync_tool_use( client, tools=[get_weather], messages=[ {"role": "user", "content": "What is the weather in SF?"}, {"role": "system", "content": [_tool_reference_block("tool_removal", "get_weather")]}, {"role": "system", "content": [_tool_reference_block("tool_addition", "get_weather")]}, ], ) assert weather_called is True assert results == [ { "role": "user", "content": [ { "type": "tool_result", "tool_use_id": "toolu_change", "content": json.dumps(_get_weather("San Francisco, CA", "f")), } ], } ] @pytest.mark.skipif(PYDANTIC_V1, reason="tool runner not supported with pydantic v1") @pytest.mark.respx(base_url=base_url) async def test_tool_removal_routes_call_down_unknown_tool_path_async(respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( side_effect=[ _tool_use_response("get_weather", "toolu_change"), _end_turn_response(), ] ) weather_called = False @beta_async_tool async def get_weather(location: str, units: Literal["c", "f"]) -> BetaFunctionToolResultType: """Lookup the weather for a given city.""" nonlocal weather_called weather_called = True return json.dumps(_get_weather(location, units)) async with AsyncAnthropic( base_url=base_url, api_key="my-anthropic-api-key", _strict_response_validation=True, max_retries=0 ) as client: runner = client.beta.messages.tool_runner( max_tokens=1024, model="claude-haiku-4-5", tools=[get_weather], messages=[ {"role": "user", "content": "What is the weather in SF?"}, {"role": "system", "content": [_tool_reference_block("tool_removal", "get_weather")]}, ], ) results: List[BetaMessageParam] = [] with pytest.warns(UserWarning, match="Tool 'get_weather' not found in tool runner"): async for _ in runner: response = await runner.generate_tool_call_response() if response is not None: results.append(response) assert weather_called is False assert results == [ { "role": "user", "content": [ { "type": "tool_result", "tool_use_id": "toolu_change", "content": "Error: Tool 'get_weather' not found", "is_error": True, } ], } ] def _not_found_result(tool_use_id: str) -> BetaMessageParam: return { "role": "user", "content": [ { "type": "tool_result", "tool_use_id": tool_use_id, "content": "Error: Tool 'get_weather' not found", "is_error": True, } ], } @pytest.mark.skipif(PYDANTIC_V1, reason="tool runner not supported with pydantic v1") @pytest.mark.respx(base_url=base_url) def test_tool_removal_via_append_messages_between_turns_sync(respx_mock: MockRouter) -> None: # The removal is not in the initial params: it is appended while iterating, on the # turn *before* the model calls the withdrawn tool. respx_mock.post("/v1/messages").mock( side_effect=[ _tool_use_response("get_time", "toolu_time", input={"timezone": "UTC"}), _tool_use_response("get_weather", "toolu_weather"), _end_turn_response(), ] ) weather_called = False @beta_tool def get_weather(location: str, units: Literal["c", "f"]) -> BetaFunctionToolResultType: """Lookup the weather for a given city.""" nonlocal weather_called weather_called = True return json.dumps(_get_weather(location, units)) @beta_tool def get_time(timezone: str) -> BetaFunctionToolResultType: """Lookup the current time in a timezone.""" return f"12:00 {timezone}" with Anthropic( base_url=base_url, api_key="my-anthropic-api-key", _strict_response_validation=True, max_retries=0 ) as client: runner = client.beta.messages.tool_runner( max_tokens=1024, model="claude-haiku-4-5", tools=[get_weather, get_time], messages=[{"role": "user", "content": "What time is it, and what is the weather in SF?"}], ) results: List[BetaMessageParam] = [] with pytest.warns(UserWarning, match="Tool 'get_weather' not found in tool runner"): for message in runner: if any(block.type == "tool_use" and block.name == "get_time" for block in message.content): # Withdraw get_weather during turn 1; the model calls it on turn 2. runner.append_messages( {"role": "system", "content": [_tool_reference_block("tool_removal", "get_weather")]} ) response = runner.generate_tool_call_response() if response is not None: results.append(response) assert weather_called is False assert results == [ {"role": "user", "content": [{"type": "tool_result", "tool_use_id": "toolu_time", "content": "12:00 UTC"}]}, _not_found_result("toolu_weather"), ] @pytest.mark.skipif(PYDANTIC_V1, reason="tool runner not supported with pydantic v1") @pytest.mark.respx(base_url=base_url) def test_tool_removal_via_append_messages_same_turn_sync(respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( side_effect=[ _tool_use_response("get_weather", "toolu_weather"), _end_turn_response(), ] ) weather_called = False @beta_tool def get_weather(location: str, units: Literal["c", "f"]) -> BetaFunctionToolResultType: """Lookup the weather for a given city.""" nonlocal weather_called weather_called = True return json.dumps(_get_weather(location, units)) with Anthropic( base_url=base_url, api_key="my-anthropic-api-key", _strict_response_validation=True, max_retries=0 ) as client: runner = client.beta.messages.tool_runner( max_tokens=1024, model="claude-haiku-4-5", tools=[get_weather], messages=[{"role": "user", "content": "What is the weather in SF?"}], ) results: List[BetaMessageParam] = [] with pytest.warns(UserWarning, match="Tool 'get_weather' not found in tool runner"): for message in runner: if any(block.type == "tool_use" for block in message.content): # The tool_use is already in `message`, but the runner has not dispatched it yet: # the loop body runs before dispatch, so a removal appended here still applies. runner.append_messages( {"role": "system", "content": [_tool_reference_block("tool_removal", "get_weather")]} ) response = runner.generate_tool_call_response() if response is not None: results.append(response) assert weather_called is False assert results == [_not_found_result("toolu_weather")] @pytest.mark.skipif(PYDANTIC_V1, reason="tool runner not supported with pydantic v1") @pytest.mark.respx(base_url=base_url) def test_tool_removal_via_set_messages_params_sync(respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( side_effect=[ _tool_use_response("get_time", "toolu_time", input={"timezone": "UTC"}), _tool_use_response("get_weather", "toolu_weather"), _end_turn_response(), ] ) weather_called = False @beta_tool def get_weather(location: str, units: Literal["c", "f"]) -> BetaFunctionToolResultType: """Lookup the weather for a given city.""" nonlocal weather_called weather_called = True return json.dumps(_get_weather(location, units)) @beta_tool def get_time(timezone: str) -> BetaFunctionToolResultType: """Lookup the current time in a timezone.""" return f"12:00 {timezone}" replacement_history: List[BetaMessageParam] = [ {"role": "user", "content": "What time is it, and what is the weather in SF?"}, {"role": "system", "content": [_tool_reference_block("tool_removal", "get_weather")]}, ] with Anthropic( base_url=base_url, api_key="my-anthropic-api-key", _strict_response_validation=True, max_retries=0 ) as client: runner = client.beta.messages.tool_runner( max_tokens=1024, model="claude-haiku-4-5", tools=[get_weather, get_time], messages=[{"role": "user", "content": "What time is it, and what is the weather in SF?"}], ) results: List[BetaMessageParam] = [] with pytest.warns(UserWarning, match="Tool 'get_weather' not found in tool runner"): for message in runner: if any(block.type == "tool_use" and block.name == "get_time" for block in message.content): # Replace the history wholesale with one carrying the removal. runner.set_messages_params(lambda params: {**params, "messages": list(replacement_history)}) response = runner.generate_tool_call_response() if response is not None: results.append(response) assert weather_called is False assert results == [ {"role": "user", "content": [{"type": "tool_result", "tool_use_id": "toolu_time", "content": "12:00 UTC"}]}, _not_found_result("toolu_weather"), ] @pytest.mark.skipif(PYDANTIC_V1, reason="tool runner not supported with pydantic v1") @pytest.mark.respx(base_url=base_url) def test_tool_addition_via_append_messages_re_enables_removed_tool_sync(respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( side_effect=[ _tool_use_response("get_weather", "toolu_weather"), _end_turn_response(), ] ) weather_called = False @beta_tool def get_weather(location: str, units: Literal["c", "f"]) -> BetaFunctionToolResultType: """Lookup the weather for a given city.""" nonlocal weather_called weather_called = True return json.dumps(_get_weather(location, units)) with Anthropic( base_url=base_url, api_key="my-anthropic-api-key", _strict_response_validation=True, max_retries=0 ) as client: runner = client.beta.messages.tool_runner( max_tokens=1024, model="claude-haiku-4-5", tools=[get_weather], messages=[ {"role": "user", "content": "What is the weather in SF?"}, {"role": "system", "content": [_tool_reference_block("tool_removal", "get_weather")]}, ], ) results: List[BetaMessageParam] = [] for message in runner: if any(block.type == "tool_use" for block in message.content): # Re-add the withdrawn tool before dispatch: it must execute normally again. runner.append_messages( {"role": "system", "content": [_tool_reference_block("tool_addition", "get_weather")]} ) response = runner.generate_tool_call_response() if response is not None: results.append(response) assert weather_called is True assert results == [ { "role": "user", "content": [ { "type": "tool_result", "tool_use_id": "toolu_weather", "content": json.dumps(_get_weather("San Francisco, CA", "f")), } ], } ] def test_tool_removal_nested_in_mid_conv_system_block() -> None: # `mid_conv_system` content is schema-limited to text/tool_addition/tool_removal, so the # one-level walk still applies a nested `tool_removal` (and ignores text). messages: List[BetaMessageParam] = [ { "role": "system", "content": [ { "type": "mid_conv_system", "content": [ {"type": "text", "text": "get_weather is no longer available."}, {"type": "tool_removal", "tool": {"type": "tool_reference", "name": "get_weather"}}, ], } ], } ] assert available_tool_names(messages, ["get_weather", "get_time"]) == {"get_time"} def test_tool_addition_nested_in_mid_conv_system_block() -> None: messages: List[BetaMessageParam] = [ {"role": "system", "content": [_tool_reference_block("tool_removal", "get_weather")]}, { "role": "system", "content": [ { "type": "mid_conv_system", "content": [ {"type": "tool_addition", "tool": {"type": "tool_reference", "name": "get_weather"}}, ], } ], }, ] assert available_tool_names(messages, ["get_weather"]) == {"get_weather"} def _get_weather(location: str, units: Literal["c", "f"]) -> Dict[str, Any]: # Simulate a weather API call print(f"Fetching weather for {location} in {units}") if units == "c": return { "location": location, "temperature": "20°C", "condition": "Sunny", } else: return { "location": location, "temperature": "68°F", "condition": "Sunny", } @pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) def test_tool_runner_method_in_sync(sync: bool, client: Anthropic, async_client: AsyncAnthropic) -> None: checking_client: "Anthropic | AsyncAnthropic" = client if sync else async_client assert_signatures_in_sync( checking_client.beta.messages.create, checking_client.beta.messages.tool_runner, exclude_params={ "tools", "output_format", # TODO "stream", }, ) anthropic-sdk-python-0.120.2/tests/lib/tools/test_session_runner.py000066400000000000000000001653341523216435200255150ustar00rootroot00000000000000"""Tests for :class:`SessionToolRunner` (the implementation behind ``client.beta.sessions.events.tool_runner()``). We use lightweight stand-ins for ``AsyncEvents`` so each test can script the sequence of stream events, list events (for the reconcile pass), and per-call send failures. ``asyncio.sleep`` is left real so ``await asyncio.sleep(0)`` in the fake stream actually yields control to the event loop. """ from __future__ import annotations import asyncio from typing import Any, Optional, cast from collections.abc import Callable, Awaitable, AsyncIterator import httpx import pytest from anthropic import APIStatusError from anthropic._compat import PYDANTIC_V1 from anthropic.lib.tools import ToolError, _beta_session_runner as session_runner_mod from anthropic.lib.tools._beta_session_runner import ( SessionToolRunner, DispatchedToolCall, ) @pytest.fixture(autouse=True) def _intercept_scoped_client(monkeypatch: pytest.MonkeyPatch) -> None: # pyright: ignore[reportUnusedFunction] """Make ``_scoped_client`` return the parent client unchanged so the runner's requests land on the test fakes. The real ``_scoped_client`` builds an ``AsyncAnthropic`` sub-client for request scoping; the tests use a ``_FakeClient`` whose only API surface is ``.beta.sessions.events``, so the sub-client construction would fail. The auth-specific tests further down install their own ``_scoped_client`` override (via the ``scoped_calls`` fixture) to assert on the args. """ def passthrough(client: Any, _key: str | None) -> Any: return client monkeypatch.setattr(session_runner_mod, "_scoped_client", passthrough) @pytest.fixture() def scoped_calls(monkeypatch: pytest.MonkeyPatch) -> list[dict[str, Any]]: """Record every ``_scoped_client(client, environment_key)`` call; still returns the parent client (the autouse fixture's behaviour) so the runner keeps using the test fakes.""" calls: list[dict[str, Any]] = [] def fake_factory(client: Any, environment_key: str | None) -> Any: calls.append({"environment_key": environment_key}) return client monkeypatch.setattr(session_runner_mod, "_scoped_client", fake_factory) return calls class _StubEvent: """Stand-in for the various session event types — only the fields the runner reads are populated.""" def __init__(self, type: str, **kw: Any) -> None: self.type = type for k, v in kw.items(): setattr(self, k, v) def _tool_use( id: str, name: str, input: dict[str, Any], *, evaluated_permission: str | None = None, ) -> _StubEvent: # ``evaluated_permission`` is always present on the real (typed) event — # ``None`` unless the server evaluated a permission policy for the call. return _StubEvent("agent.tool_use", id=id, name=name, input=input, evaluated_permission=evaluated_permission) def _tool_result(tool_use_id: str) -> _StubEvent: return _StubEvent("user.tool_result", tool_use_id=tool_use_id) def _tool_confirmation(tool_use_id: str, result: str) -> _StubEvent: """The user's allow/deny verdict for an ask-gated (``always_ask``) tool call.""" return _StubEvent("user.tool_confirmation", id=f"conf_{tool_use_id}", tool_use_id=tool_use_id, result=result) def _custom_tool_use(id: str, name: str, input: dict[str, Any]) -> _StubEvent: """A CUSTOM (user-defined) tool call — the agent emits ``agent.custom_tool_use`` rather than ``agent.tool_use`` for these.""" return _StubEvent("agent.custom_tool_use", id=id, name=name, input=input) def _custom_tool_result(custom_tool_use_id: str) -> _StubEvent: return _StubEvent("user.custom_tool_result", custom_tool_use_id=custom_tool_use_id) def _terminated() -> _StubEvent: return _StubEvent("session.status_terminated") def _idle_end_turn() -> _StubEvent: return _StubEvent("session.status_idle", stop_reason=_StubEvent("end_turn")) def _result_content(item: DispatchedToolCall) -> Any: """The content blocks the runner computed and posted back, as carried in ``result`` (the flat ``content`` convenience field was removed).""" return cast(Any, item.result)["content"] def _result_text(item: DispatchedToolCall) -> str: """Concatenated text of the posted-back result's text blocks.""" return "".join(b.get("text", "") for b in _result_content(item) if b.get("type") == "text") def _api_status_error(code: int) -> APIStatusError: request = httpx.Request("POST", "https://api.example/x") response = httpx.Response(status_code=code, request=request, content=b"{}") return APIStatusError("boom", response=response, body=None) class _FakeStream: """Stand-in for the AsyncStream returned by ``events.stream()``. Yields scripted events in order; once exhausted, blocks forever (the real stream stays open until a network event closes it). If ``raise_after`` is set, raises ``raise_with`` after producing that many events — used to exercise the reconnect-with-backoff path. """ def __init__( self, events: list[_StubEvent], *, raise_after: int | None = None, raise_with: BaseException | None = None, ) -> None: self._events = events self._raise_after = raise_after self._raise_with = raise_with async def __aenter__(self) -> _FakeStream: return self async def __aexit__(self, *exc: object) -> None: return None def __aiter__(self) -> Any: return self._gen() async def _gen(self) -> Any: for i, ev in enumerate(self._events): yield ev # Yield control so the dispatch task can pick up the event we just # produced before we run on to the next one. await asyncio.sleep(0) if self._raise_after is not None and i + 1 == self._raise_after: assert self._raise_with is not None raise self._raise_with # Keep the connection "open" until cancelled. await asyncio.Event().wait() class FakeAsyncEvents: def __init__( self, *, streams: list[_FakeStream | BaseException] | None = None, stream_events: list[_StubEvent] | None = None, list_events: list[_StubEvent] | None = None, list_events_per_call: list[list[_StubEvent]] | None = None, list_raises: BaseException | None = None, send_failures: list[BaseException | None] | None = None, ) -> None: if streams is not None: self._streams: list[_FakeStream | BaseException] = list(streams) elif stream_events is not None: self._streams = [_FakeStream(stream_events)] else: self._streams = [_FakeStream([])] self._list_events = list(list_events or []) # When set, each ``list()`` call consumes the next entry (falling back # to ``list_events`` once exhausted) so reconnect tests can script a # different history per reconcile pass. self._list_events_per_call = [list(evs) for evs in (list_events_per_call or [])] self._list_raises = list_raises self._send_failures: list[BaseException | None] = list(send_failures or []) self.send_calls: list[dict[str, Any]] = [] self.stream_calls: int = 0 self.stream_headers: list[Any] = [] self.list_headers: list[Any] = [] async def stream(self, _session_id: str, *, extra_headers: Any = None) -> _FakeStream: self.stream_calls += 1 self.stream_headers.append(extra_headers) if not self._streams: return _FakeStream([]) # block forever; mirrors a fresh connection nxt = self._streams.pop(0) if isinstance(nxt, BaseException): raise nxt return nxt def list(self, _session_id: str, *, limit: int = 1000, extra_headers: Any = None) -> Any: # noqa: ARG002 list_raises = self._list_raises self.list_headers.append(extra_headers) list_events = self._list_events_per_call.pop(0) if self._list_events_per_call else self._list_events async def _gen() -> Any: for ev in list_events: yield ev if list_raises is not None: raise list_raises return _gen() async def send(self, session_id: str, *, events: list[Any], extra_headers: Any = None) -> None: idx = len(self.send_calls) self.send_calls.append({"session_id": session_id, "events": events, "extra_headers": extra_headers}) if idx < len(self._send_failures): err = self._send_failures[idx] if err is not None: raise err class _FakeTool: def __init__( self, name: str, fn: Callable[[dict[str, Any]], Awaitable[Any]], *, close: Callable[[], Any] | None = None, ) -> None: self.name = name self._fn = fn if close is not None: self.close = close def call(self, input: dict[str, Any]) -> Any: return self._fn(input) class _FakeClient: """Minimal stand-in for ``AsyncAnthropic`` — only exposes the resource path the runner reads.""" def __init__(self, events: FakeAsyncEvents) -> None: self.beta = type( "_Beta", (), {"sessions": type("_Sessions", (), {"events": events})()}, )() async def _run_with_fakes( *, events: FakeAsyncEvents, tools: list[Any], max_idle: float | None = None, environment_key: str | None = None, extra_headers: dict[str, Any] | None = None, ) -> AsyncIterator[DispatchedToolCall]: client = _FakeClient(events) runner = SessionToolRunner( cast(Any, client), "s_1", tools=tools, max_idle=max_idle, environment_key=environment_key, extra_headers=extra_headers, ) async for call in runner: yield call # ---------- happy-path / basic termination --------------------------------- @pytest.mark.asyncio() async def test_yields_completed_tool_call() -> None: async def echo(input: dict[str, Any]) -> str: return f"got {input.get('x')}" tool = _FakeTool("echo", echo) events = FakeAsyncEvents(stream_events=[_tool_use("tu_1", "echo", {"x": 42}), _terminated()]) items = [item async for item in _run_with_fakes(events=events, tools=[tool])] assert len(items) == 1 item = items[0] assert isinstance(item, DispatchedToolCall) assert item.tool_use_id == "tu_1" assert item.name == "echo" assert item.event.input == {"x": 42} assert item.is_error is False assert item.posted is True assert _result_text(item) == "got 42" # The reshaped DispatchedToolCall embeds the originating event and the # posted-back result block, alongside the flat convenience fields. assert item.event.id == "tu_1" assert item.result is not None assert item.result["type"] == "user.tool_result" assert item.result["tool_use_id"] == "tu_1" assert item.result.get("is_error") is False assert len(events.send_calls) == 1 sent = events.send_calls[0]["events"][0] assert sent["type"] == "user.tool_result" assert sent["tool_use_id"] == "tu_1" assert sent["is_error"] is False @pytest.mark.asyncio() async def test_yields_error_for_failing_tool() -> None: async def boom(_input: dict[str, Any]) -> str: raise RuntimeError("nope") tool = _FakeTool("boom", boom) events = FakeAsyncEvents(stream_events=[_tool_use("tu_1", "boom", {}), _terminated()]) items = [item async for item in _run_with_fakes(events=events, tools=[tool])] assert len(items) == 1 assert items[0].is_error is True assert items[0].posted is True assert "RuntimeError" in _result_text(items[0]) @pytest.mark.asyncio() async def test_unknown_tool_skipped_by_default() -> None: """An unregistered tool name is assumed to belong to the other client servicing the session, so it is skipped — not answered in place. The call is still yielded (``posted=False`` / ``is_error=False`` / ``result=None``) and nothing is posted.""" events = FakeAsyncEvents(stream_events=[_tool_use("tu_1", "missing", {}), _terminated()]) items = [item async for item in _run_with_fakes(events=events, tools=[])] assert len(items) == 1 assert items[0].is_error is False assert items[0].posted is False assert items[0].result is None assert events.send_calls == [] @pytest.mark.asyncio() async def test_skips_already_answered_events() -> None: """Tool result already in history (via reconcile) should suppress re-execution of the same tool_use seen on the live stream.""" counter = {"calls": 0} async def increment(_input: dict[str, Any]) -> str: counter["calls"] += 1 return "done" tool = _FakeTool("inc", increment) events = FakeAsyncEvents( list_events=[_tool_use("tu_1", "inc", {}), _tool_result("tu_1")], stream_events=[_tool_use("tu_1", "inc", {}), _terminated()], ) items = [item async for item in _run_with_fakes(events=events, tools=[tool])] assert counter["calls"] == 0 assert items == [] # ---------- custom-tool dispatch ------------------------------------------ @pytest.mark.asyncio() async def test_yields_completed_custom_tool_call() -> None: """A CUSTOM (user-defined) tool call arrives as ``agent.custom_tool_use`` and must be answered with ``user.custom_tool_result`` — keyed by ``custom_tool_use_id`` — not ``user.tool_result``.""" async def weather(input: dict[str, Any]) -> str: return f"sunny in {input.get('city')}" tool = _FakeTool("get_weather", weather) events = FakeAsyncEvents(stream_events=[_custom_tool_use("ctu_1", "get_weather", {"city": "SF"}), _terminated()]) items = [item async for item in _run_with_fakes(events=events, tools=[tool])] assert len(items) == 1 item = items[0] assert item.tool_use_id == "ctu_1" assert item.name == "get_weather" assert item.event.input == {"city": "SF"} assert item.is_error is False assert item.posted is True assert _result_text(item) == "sunny in SF" # The embedded event is the custom-tool-use event, and the posted-back # result is a custom tool result keyed by custom_tool_use_id. assert item.event.type == "agent.custom_tool_use" result = cast(Any, item.result) assert result["type"] == "user.custom_tool_result" assert result["custom_tool_use_id"] == "ctu_1" assert result.get("is_error") is False assert len(events.send_calls) == 1 sent = events.send_calls[0]["events"][0] assert sent["type"] == "user.custom_tool_result" assert sent["custom_tool_use_id"] == "ctu_1" assert sent["is_error"] is False @pytest.mark.asyncio() async def test_dispatches_builtin_and_custom_tools_in_one_stream() -> None: """A single stream carrying both an ``agent.tool_use`` and an ``agent.custom_tool_use`` dispatches both, each answered with its matching result-event type.""" async def echo(input: dict[str, Any]) -> str: return f"echo {input.get('x')}" async def weather(_input: dict[str, Any]) -> str: return "sunny" events = FakeAsyncEvents( stream_events=[ _tool_use("tu_1", "echo", {"x": 1}), _custom_tool_use("ctu_1", "get_weather", {}), _terminated(), ] ) items = [ item async for item in _run_with_fakes( events=events, tools=[_FakeTool("echo", echo), _FakeTool("get_weather", weather)] ) ] by_id = {it.tool_use_id: it for it in items} assert set(by_id) == {"tu_1", "ctu_1"} builtin_result = by_id["tu_1"].result custom_result = by_id["ctu_1"].result assert builtin_result is not None and custom_result is not None assert by_id["tu_1"].event.type == "agent.tool_use" assert builtin_result["type"] == "user.tool_result" assert by_id["ctu_1"].event.type == "agent.custom_tool_use" assert custom_result["type"] == "user.custom_tool_result" # Both result events were posted, each with the matching type. posted = {call["events"][0]["type"] for call in events.send_calls} assert posted == {"user.tool_result", "user.custom_tool_result"} @pytest.mark.asyncio() async def test_skips_already_answered_custom_tool() -> None: """A custom tool whose ``user.custom_tool_result`` is already in history (via reconcile) is not re-executed when the same ``agent.custom_tool_use`` is then seen on the live stream.""" counter = {"calls": 0} async def weather(_input: dict[str, Any]) -> str: counter["calls"] += 1 return "sunny" events = FakeAsyncEvents( list_events=[_custom_tool_use("ctu_1", "get_weather", {}), _custom_tool_result("ctu_1")], stream_events=[_custom_tool_use("ctu_1", "get_weather", {}), _terminated()], ) items = [item async for item in _run_with_fakes(events=events, tools=[_FakeTool("get_weather", weather)])] assert counter["calls"] == 0 assert items == [] # ---------- skip unowned tools (split-client partial fulfilment) ----------- @pytest.mark.asyncio() async def test_skips_unowned_builtin_and_custom_tools_by_default() -> None: """Default split-client behavior: a tool-call event whose name is not in the runner's registry belongs to the other client servicing the session (e.g. the customer's app backend handling custom tools). The runner must post NO result for it, claim nothing, and leave the ``tool_use_id`` pending — while still yielding the ``DispatchedToolCall`` so the caller can observe the unowned dispatch (``posted=False``, ``is_error=False``, ``result=None``). A registered tool in the same stream still runs, and the registry miss must not raise.""" ran = {"echo": 0} async def echo(_input: dict[str, Any]) -> str: ran["echo"] += 1 return "ok" events = FakeAsyncEvents( stream_events=[ _tool_use("evt_99", "not_ours", {}), _custom_tool_use("cevt_99", "app_backend_tool", {}), _tool_use("tu_ok", "echo", {}), _terminated(), ] ) items = [item async for item in _run_with_fakes(events=events, tools=[_FakeTool("echo", echo)])] by_id = {it.tool_use_id: it for it in items} assert set(by_id) == {"evt_99", "cevt_99", "tu_ok"} builtin = by_id["evt_99"] assert builtin.name == "not_ours" assert builtin.event.type == "agent.tool_use" assert builtin.is_error is False, "a skipped call is not an error" assert builtin.posted is False, "nothing was sent for an unowned tool" assert builtin.result is None, "no user.tool_result was ever built" custom = by_id["cevt_99"] assert custom.name == "app_backend_tool" assert custom.event.type == "agent.custom_tool_use" assert custom.is_error is False, "a skipped call is not an error" assert custom.posted is False, "nothing was sent for an unowned custom tool" assert custom.result is None, "no user.custom_tool_result was ever built" owned = by_id["tu_ok"] assert owned.is_error is False assert owned.posted is True assert owned.result is not None and owned.result["type"] == "user.tool_result" assert ran["echo"] == 1, "the registered tool should still have run" # Only the owned tool's result reached the session; nothing for the unowned. assert len(events.send_calls) == 1 assert events.send_calls[0]["events"][0]["tool_use_id"] == "tu_ok" @pytest.mark.asyncio() async def test_skipped_unowned_tool_does_not_trip_idle() -> None: """A skipped (unanswered) unowned tool_use stays OUT of the end-turn accounting: reconcile sees history ending on an ``end_turn`` idle but with the unowned tool_use still unanswered, so it must NOT arm the idle countdown — the runner has not handled that call, its owner still has to. A correct runner therefore stays alive past ``max_idle`` (the iterator never completes); a buggy one would idle-stop almost immediately. """ events = FakeAsyncEvents( # No live events — the reconcile pass drives the test. History ends on # an end_turn idle with the unowned tool_use still unanswered. list_events=[_tool_use("evt_pending", "not_ours", {}), _idle_end_turn()], stream_events=[], ) seen: list[DispatchedToolCall] = [] async def drive() -> None: async for call in _run_with_fakes(events=events, tools=[], max_idle=0.1): seen.append(call) # If the unowned tool wrongly armed the idle clock the runner would stop # ~0.1s in and ``drive()`` would return; a correct runner blocks until the # (never-arriving) owner answers, so ``wait_for`` must time out instead. with pytest.raises((asyncio.TimeoutError, TimeoutError)): await asyncio.wait_for(drive(), timeout=1.0) assert len(seen) >= 1, "reconcile must still surface the unowned call" call = seen[0] assert call.tool_use_id == "evt_pending" assert call.posted is False assert call.is_error is False assert call.result is None, "no result was built for the skipped call" assert events.send_calls == [], "runner must not post a result it does not own" # ---------- confirmation gating (always_ask tools) -------------------------- @pytest.mark.asyncio() async def test_ask_tool_blocks_without_confirmation() -> None: """An ``agent.tool_use`` whose ``evaluated_permission`` is ``ask`` (an ``always_ask`` tool) must NOT execute on arrival — it is held until the matching ``user.tool_confirmation`` event. Here none ever arrives, so the tool never runs, nothing is posted, and nothing is yielded.""" counter = {"calls": 0} async def gated(_input: dict[str, Any]) -> str: counter["calls"] += 1 return "ran" tool = _FakeTool("gated", gated) events = FakeAsyncEvents(stream_events=[_tool_use("tu_1", "gated", {}, evaluated_permission="ask"), _terminated()]) items = [item async for item in _run_with_fakes(events=events, tools=[tool])] assert counter["calls"] == 0, "an ask-gated tool must not run before its confirmation" assert events.send_calls == [], "no result may be posted for an unconfirmed call" assert items == [] @pytest.mark.asyncio() async def test_ask_tool_executes_after_allow_confirmation() -> None: """An ``allow`` confirmation releases the held call: the tool runs, the result is posted, and the yielded call records ``confirmation="allow"``.""" counter = {"calls": 0} async def gated(_input: dict[str, Any]) -> str: counter["calls"] += 1 return "ran" tool = _FakeTool("gated", gated) events = FakeAsyncEvents( stream_events=[ _tool_use("tu_1", "gated", {}, evaluated_permission="ask"), _tool_confirmation("tu_1", "allow"), _terminated(), ] ) items = [item async for item in _run_with_fakes(events=events, tools=[tool])] assert counter["calls"] == 1 assert len(items) == 1 call = items[0] assert call.confirmation == "allow" assert call.posted is True assert call.is_error is False assert _result_text(call) == "ran" assert len(events.send_calls) == 1 assert events.send_calls[0]["events"][0]["tool_use_id"] == "tu_1" @pytest.mark.asyncio() async def test_ask_tool_denied_never_executes() -> None: """A ``deny`` confirmation resolves the held call without executing it: nothing runs, nothing is posted (the denial itself resolves the call server-side), and the call is still yielded for observability with ``confirmation="deny"`` / ``posted=False`` / ``result=None``.""" counter = {"calls": 0} async def gated(_input: dict[str, Any]) -> str: counter["calls"] += 1 return "ran" tool = _FakeTool("gated", gated) events = FakeAsyncEvents( stream_events=[ _tool_use("tu_1", "gated", {}, evaluated_permission="ask"), _tool_confirmation("tu_1", "deny"), _terminated(), ] ) items = [item async for item in _run_with_fakes(events=events, tools=[tool])] assert counter["calls"] == 0, "a denied tool must never run" assert events.send_calls == [], "the denial resolves the call; the runner must post nothing" assert len(items) == 1 call = items[0] assert call.confirmation == "deny" assert call.posted is False assert call.is_error is False assert call.result is None @pytest.mark.asyncio() async def test_pre_denied_tool_never_executes() -> None: """A call the server already evaluated to ``deny`` needs no confirmation — it must never execute and nothing may be posted for it.""" counter = {"calls": 0} async def gated(_input: dict[str, Any]) -> str: counter["calls"] += 1 return "ran" tool = _FakeTool("gated", gated) events = FakeAsyncEvents(stream_events=[_tool_use("tu_1", "gated", {}, evaluated_permission="deny"), _terminated()]) items = [item async for item in _run_with_fakes(events=events, tools=[tool])] assert counter["calls"] == 0 assert events.send_calls == [] assert len(items) == 1 assert items[0].confirmation == "deny" assert items[0].posted is False assert items[0].result is None @pytest.mark.asyncio() async def test_confirmation_in_history_releases_ask_call() -> None: """An ask-gated call whose ``allow`` confirmation is already in history (e.g. it was posted while the runner was disconnected) executes on the reconcile pass — the verdict is recorded before pending calls are routed.""" counter = {"calls": 0} async def gated(_input: dict[str, Any]) -> str: counter["calls"] += 1 return "ran" tool = _FakeTool("gated", gated) events = FakeAsyncEvents( list_events=[ _tool_use("tu_1", "gated", {}, evaluated_permission="ask"), _tool_confirmation("tu_1", "allow"), ], stream_events=[_terminated()], ) items = [item async for item in _run_with_fakes(events=events, tools=[tool])] assert counter["calls"] == 1 assert len(items) == 1 assert items[0].confirmation == "allow" assert items[0].posted is True @pytest.mark.asyncio() async def test_denied_ask_call_does_not_block_idle_stop() -> None: """A denied call counts as resolved in the reconcile idle accounting: history ends on an ``end_turn`` idle with the denied call unanswered (no result event ever exists for it), and the runner must still arm the idle countdown and stop on its own rather than wait forever for a result.""" events = FakeAsyncEvents( list_events=[ _tool_use("tu_1", "gated", {}, evaluated_permission="ask"), _tool_confirmation("tu_1", "deny"), _idle_end_turn(), ], stream_events=[], ) items = [item async for item in _run_with_fakes(events=events, tools=[], max_idle=0.05)] assert len(items) == 1 assert items[0].confirmation == "deny" assert events.send_calls == [] @pytest.mark.asyncio() async def test_held_ask_call_keeps_runner_alive() -> None: """While a call awaits its confirmation the runner must keep running — even if history (defensively) ends on an ``end_turn`` idle — so the verdict can still arrive and be acted on.""" counter = {"calls": 0} async def gated(_input: dict[str, Any]) -> str: counter["calls"] += 1 return "ran" tool = _FakeTool("gated", gated) events = FakeAsyncEvents( list_events=[_tool_use("tu_1", "gated", {}, evaluated_permission="ask"), _idle_end_turn()], stream_events=[], ) async def drive() -> None: async for _ in _run_with_fakes(events=events, tools=[tool], max_idle=0.1): pass # A runner that wrongly armed the idle clock would stop ~0.1s in and # ``drive()`` would return; a correct one blocks awaiting the confirmation. with pytest.raises((asyncio.TimeoutError, TimeoutError)): await asyncio.wait_for(drive(), timeout=1.0) assert counter["calls"] == 0 assert events.send_calls == [] @pytest.mark.asyncio() async def test_live_idle_while_call_held_keeps_runner_alive() -> None: """An ``end_turn`` idle arriving on the LIVE stream while a call is held for confirmation must not start the idle countdown — stopping would drop the call when its verdict later arrives. (The reconcile-path counterpart is ``test_held_ask_call_keeps_runner_alive``.)""" counter = {"calls": 0} async def gated(_input: dict[str, Any]) -> str: counter["calls"] += 1 return "ran" tool = _FakeTool("gated", gated) events = FakeAsyncEvents( stream_events=[_tool_use("tu_1", "gated", {}, evaluated_permission="ask"), _idle_end_turn()], ) async def drive() -> None: async for _ in _run_with_fakes(events=events, tools=[tool], max_idle=0.1): pass # A runner that armed the idle clock would stop ~0.1s in and ``drive()`` # would return; a correct one blocks awaiting the confirmation. with pytest.raises((asyncio.TimeoutError, TimeoutError)): await asyncio.wait_for(drive(), timeout=1.0) assert counter["calls"] == 0 assert events.send_calls == [] @pytest.mark.asyncio() async def test_reconnect_does_not_double_dispatch_held_call(monkeypatch: pytest.MonkeyPatch) -> None: """A call held on the live stream whose ``allow`` confirmation shows up in the reconcile history after a reconnect is dispatched exactly once: the routing pass applies the recorded verdict, the history loop must not also release the held copy. The first result post fails permanently so a duplicate enqueue would visibly re-execute the tool.""" monkeypatch.setattr(session_runner_mod, "STREAM_BACKOFF_START", 0.01) counter = {"calls": 0} async def gated(_input: dict[str, Any]) -> str: counter["calls"] += 1 return "ran" tool = _FakeTool("gated", gated) gated_call = _tool_use("tu_1", "gated", {}, evaluated_permission="ask") events = FakeAsyncEvents( streams=[ # First connection: the gated call arrives live (and is held), then # the stream drops with a transient error. _FakeStream([gated_call], raise_after=1, raise_with=httpx.ReadError("dropped")), _FakeStream([_terminated()]), ], # The reconcile after the reconnect sees both the held call and its # allow verdict; the initial reconcile saw an empty history. list_events_per_call=[[], [gated_call, _tool_confirmation("tu_1", "allow")]], send_failures=[_api_status_error(400)], ) items = [item async for item in _run_with_fakes(events=events, tools=[tool])] assert counter["calls"] == 1, "the confirmed call must be dispatched exactly once" assert len([it for it in items if it.tool_use_id == "tu_1"]) == 1 @pytest.mark.asyncio() async def test_reconcile_confirmation_releases_call_held_from_live_stream(monkeypatch: pytest.MonkeyPatch) -> None: """A confirmation that only ever appears in the reconcile history (its tool_use event is not in the listed window — it was held from the live stream before the disconnect) still releases the held call.""" monkeypatch.setattr(session_runner_mod, "STREAM_BACKOFF_START", 0.01) counter = {"calls": 0} async def gated(_input: dict[str, Any]) -> str: counter["calls"] += 1 return "ran" tool = _FakeTool("gated", gated) events = FakeAsyncEvents( streams=[ _FakeStream( [_tool_use("tu_1", "gated", {}, evaluated_permission="ask")], raise_after=1, raise_with=httpx.ReadError("dropped"), ), _FakeStream([_terminated()]), ], list_events_per_call=[[], [_tool_confirmation("tu_1", "allow")]], ) items = [item async for item in _run_with_fakes(events=events, tools=[tool])] assert counter["calls"] == 1 assert len(items) == 1 assert items[0].confirmation == "allow" assert items[0].posted is True @pytest.mark.asyncio() async def test_held_ask_call_does_not_block_other_dispatches() -> None: """Holding an ask-gated call must not stall the rest of the queue: an ungated call arriving after it executes immediately, and the gated call follows once its confirmation lands.""" async def echo(_input: dict[str, Any]) -> str: return "echo" async def gated(_input: dict[str, Any]) -> str: return "gated" events = FakeAsyncEvents( stream_events=[ _tool_use("tu_gated", "gated", {}, evaluated_permission="ask"), _tool_use("tu_echo", "echo", {}), _tool_confirmation("tu_gated", "allow"), _terminated(), ] ) items = [ item async for item in _run_with_fakes(events=events, tools=[_FakeTool("echo", echo), _FakeTool("gated", gated)]) ] by_id = {it.tool_use_id: it for it in items} assert set(by_id) == {"tu_gated", "tu_echo"} assert by_id["tu_echo"].confirmation is None assert by_id["tu_echo"].posted is True assert by_id["tu_gated"].confirmation == "allow" assert by_id["tu_gated"].posted is True # The ungated call was not held up behind the gated one: its result was # posted first, the gated one only after its confirmation arrived. posted_ids = [call["events"][0]["tool_use_id"] for call in events.send_calls] assert posted_ids == ["tu_echo", "tu_gated"] @pytest.mark.asyncio() async def test_confirmation_for_unknown_id_is_ignored() -> None: """A confirmation for a call this runner has never seen (another client's call, or an ``agent.mcp_tool_use`` it never dispatches) is recorded but must not crash or yield anything.""" events = FakeAsyncEvents(stream_events=[_tool_confirmation("tu_elsewhere", "allow"), _terminated()]) items = [item async for item in _run_with_fakes(events=events, tools=[])] assert items == [] assert events.send_calls == [] @pytest.mark.asyncio() async def test_unrecognised_verdict_fails_closed() -> None: """The gate is an allow-list: a confirmation whose ``result`` is a value this SDK doesn't recognise (the wire can carry values newer than our types) must NOT release the held call — it is resolved as a denial.""" counter = {"calls": 0} async def gated(_input: dict[str, Any]) -> str: counter["calls"] += 1 return "ran" tool = _FakeTool("gated", gated) events = FakeAsyncEvents( stream_events=[ _tool_use("tu_1", "gated", {}, evaluated_permission="ask"), _tool_confirmation("tu_1", "escalate"), # not "allow"/"deny" _terminated(), ] ) items = [item async for item in _run_with_fakes(events=events, tools=[tool])] assert counter["calls"] == 0, "only an explicit allow may release a gated call" assert events.send_calls == [] assert len(items) == 1 assert items[0].confirmation == "deny" assert items[0].result is None @pytest.mark.asyncio() async def test_unrecognised_permission_fails_closed() -> None: """An ``evaluated_permission`` value this SDK doesn't recognise must not dispatch unconfirmed — it is held like ``ask`` and released only by an explicit ``allow`` verdict.""" counter = {"calls": 0} async def gated(_input: dict[str, Any]) -> str: counter["calls"] += 1 return "ran" tool = _FakeTool("gated", gated) # Without a confirmation the call must never run... events = FakeAsyncEvents( stream_events=[_tool_use("tu_1", "gated", {}, evaluated_permission="ask_strict"), _terminated()] ) items = [item async for item in _run_with_fakes(events=events, tools=[tool])] assert counter["calls"] == 0 assert items == [] # ...while an explicit allow still releases it. events = FakeAsyncEvents( stream_events=[ _tool_use("tu_2", "gated", {}, evaluated_permission="ask_strict"), _tool_confirmation("tu_2", "allow"), _terminated(), ] ) items = [item async for item in _run_with_fakes(events=events, tools=[tool])] assert counter["calls"] == 1 assert len(items) == 1 assert items[0].posted is True @pytest.mark.asyncio() async def test_pre_denied_tool_ignores_stray_allow_verdict() -> None: """A call the server already evaluated to ``deny`` must never execute, even if an (anomalous) ``allow`` confirmation exists for its id.""" counter = {"calls": 0} async def gated(_input: dict[str, Any]) -> str: counter["calls"] += 1 return "ran" tool = _FakeTool("gated", gated) events = FakeAsyncEvents( stream_events=[ # Confirmation first so the verdict is already recorded when the # pre-denied call is routed. _tool_confirmation("tu_1", "allow"), _tool_use("tu_1", "gated", {}, evaluated_permission="deny"), _terminated(), ] ) items = [item async for item in _run_with_fakes(events=events, tools=[tool])] assert counter["calls"] == 0, "a server-denied call must never execute" assert events.send_calls == [] assert len(items) == 1 assert items[0].confirmation == "deny" @pytest.mark.asyncio() async def test_ungated_tool_with_stray_deny_verdict_resolves_as_denied() -> None: """Mirror of ``test_pre_denied_tool_ignores_stray_allow_verdict``: a stray ``deny`` verdict recorded before an ungated call is routed resolves the call as denied without executing it — any deny signal wins (the gate fails closed).""" counter = {"calls": 0} async def echo(_input: dict[str, Any]) -> str: counter["calls"] += 1 return "ran" tool = _FakeTool("echo", echo) events = FakeAsyncEvents( stream_events=[ # Confirmation first so the stray verdict is already recorded when # the ungated call is routed. _tool_confirmation("tu_1", "deny"), _tool_use("tu_1", "echo", {}), _terminated(), ] ) items = [item async for item in _run_with_fakes(events=events, tools=[tool])] assert counter["calls"] == 0, "a deny verdict must suppress the call even if it was never gated" assert events.send_calls == [] assert len(items) == 1 assert items[0].confirmation == "deny" assert items[0].posted is False assert items[0].result is None @pytest.mark.asyncio() async def test_deny_after_live_end_turn_resumes_idle_stop() -> None: """A ``deny`` that resolves the last held call must let the idle countdown resume: the session already went idle (``end_turn``) while the call was held, the denial produces no further stream events, and the runner must stop on its own instead of waiting forever.""" counter = {"calls": 0} async def gated(_input: dict[str, Any]) -> str: counter["calls"] += 1 return "ran" tool = _FakeTool("gated", gated) events = FakeAsyncEvents( stream_events=[ _tool_use("tu_1", "gated", {}, evaluated_permission="ask"), _idle_end_turn(), _tool_confirmation("tu_1", "deny"), ] ) async def drive() -> list[DispatchedToolCall]: return [item async for item in _run_with_fakes(events=events, tools=[tool], max_idle=0.05)] # A runner that lost the end_turn while the call was held hangs here (the # stream never produces another event) and ``wait_for`` would time out. items = await asyncio.wait_for(drive(), timeout=2.0) assert counter["calls"] == 0 assert events.send_calls == [] assert len(items) == 1 assert items[0].confirmation == "deny" @pytest.mark.asyncio() async def test_reconcile_released_call_not_cut_short_by_idle(monkeypatch: pytest.MonkeyPatch) -> None: """A held call released by the reconcile pass (its allow verdict only shows up in history after a reconnect) is in-flight work: even if that history ends on an ``end_turn`` idle, the idle countdown must not run while the released tool is still executing. Only once the call is fully dispatched does the deferred countdown start, granting a fresh grace window for the events its posted result will produce.""" monkeypatch.setattr(session_runner_mod, "STREAM_BACKOFF_START", 0.01) max_idle = 0.4 counter = {"calls": 0} async def slow_gated(_input: dict[str, Any]) -> str: counter["calls"] += 1 await asyncio.sleep(0.6) return "ran" tool = _FakeTool("gated", slow_gated) events = FakeAsyncEvents( streams=[ # The gated call arrives live (and is held), then the stream drops. _FakeStream( [_tool_use("tu_1", "gated", {}, evaluated_permission="ask")], raise_after=1, raise_with=httpx.ReadError("dropped"), ), _FakeStream([]), ], # The reconcile after the reconnect sees only the verdict and the idle: # the original tool_use event has scrolled out of the listed window. list_events_per_call=[[], [_tool_confirmation("tu_1", "allow"), _idle_end_turn()]], ) seen: list[DispatchedToolCall] = [] async def drive() -> None: async for call in _run_with_fakes(events=events, tools=[tool], max_idle=max_idle): seen.append(call) async def run_and_time() -> tuple[float, float]: loop = asyncio.get_running_loop() task = asyncio.ensure_future(drive()) while not events.send_calls: await asyncio.sleep(0.01) posted_at = loop.time() await task return posted_at, loop.time() posted_at, stopped_at = await asyncio.wait_for(run_and_time(), timeout=5.0) assert counter["calls"] == 1 assert len(seen) == 1 assert seen[0].confirmation == "allow" assert seen[0].posted is True # A runner that armed the idle clock during the reconcile has its countdown # already expired by the time the slow tool finishes (0.6s > max_idle) and # stops immediately after posting; the deferred countdown instead starts # only once the call is dispatched, so the runner stays up for roughly a # full grace window after the post. assert stopped_at - posted_at > max_idle * 0.6 @pytest.mark.asyncio() async def test_idle_after_end_turn_ends_iteration() -> None: # The session goes idle with stop_reason end_turn and nothing else happens; # after ``max_idle`` seconds the runner stops on its own. events = FakeAsyncEvents(stream_events=[_idle_end_turn()]) items = [item async for item in _run_with_fakes(events=events, tools=[], max_idle=0.05)] assert items == [] @pytest.mark.asyncio() async def test_idle_grace_does_not_fire_without_end_turn() -> None: # No end_turn idle is ever seen, so the grace timer never arms — the runner # only stops because the stream delivers a terminated event. async def echo(input: dict[str, Any]) -> str: return f"got {input.get('x')}" events = FakeAsyncEvents(stream_events=[_tool_use("tu_1", "echo", {"x": 1}), _terminated()]) items = [item async for item in _run_with_fakes(events=events, tools=[_FakeTool("echo", echo)], max_idle=0.05)] assert [it.tool_use_id for it in items] == ["tu_1"] @pytest.mark.asyncio() async def test_new_event_resets_idle_grace() -> None: # end_turn arms the grace timer, then a tool_use arrives and resets it; the # tool is dispatched and the runner only stops on the terminated event. async def echo(input: dict[str, Any]) -> str: return f"got {input.get('x')}" events = FakeAsyncEvents(stream_events=[_idle_end_turn(), _tool_use("tu_1", "echo", {"x": 1}), _terminated()]) # Generous grace so the timer can't fire between the scripted events. items = [item async for item in _run_with_fakes(events=events, tools=[_FakeTool("echo", echo)], max_idle=5.0)] assert [it.tool_use_id for it in items] == ["tu_1"] @pytest.mark.asyncio() async def test_terminated_event_ends_iteration() -> None: events = FakeAsyncEvents(stream_events=[_terminated()]) items = [item async for item in _run_with_fakes(events=events, tools=[])] assert items == [] # ---------- tool cleanup -------------------------------------------------- @pytest.mark.asyncio() async def test_runs_tool_close_hook_on_exit() -> None: """The runner calls each tool's optional ``close`` cleanup hook when the iteration ends, regardless of cause.""" closed = {"count": 0} def _close() -> None: closed["count"] += 1 async def echo(_input: dict[str, Any]) -> str: return "ok" tool = _FakeTool("echo", echo, close=_close) events = FakeAsyncEvents(stream_events=[_tool_use("tu_1", "echo", {}), _terminated()]) [_ async for _ in _run_with_fakes(events=events, tools=[tool])] assert closed["count"] == 1 @pytest.mark.asyncio() async def test_awaits_async_tool_close_hook() -> None: closed = {"count": 0} async def _aclose() -> None: closed["count"] += 1 async def echo(_input: dict[str, Any]) -> str: return "ok" tool = _FakeTool("echo", echo, close=_aclose) events = FakeAsyncEvents(stream_events=[_terminated()]) [_ async for _ in _run_with_fakes(events=events, tools=[tool])] assert closed["count"] == 1 # ---------- send-result failure surfaces to consumer ----------------------- @pytest.mark.asyncio() async def test_yields_with_posted_false_on_retry_exhaust() -> None: """If ``events.send`` fails on every retry attempt, the consumer should still receive the ``DispatchedToolCall`` with ``posted=False`` so they know the tool ran but the session-side agent never saw the result.""" async def echo(_input: dict[str, Any]) -> str: return "result" tool = _FakeTool("echo", echo) events = FakeAsyncEvents( stream_events=[_tool_use("tu_1", "echo", {}), _terminated()], send_failures=[_api_status_error(500), _api_status_error(500), _api_status_error(500)], ) items = [item async for item in _run_with_fakes(events=events, tools=[tool])] assert len(items) == 1 assert items[0].posted is False # The tool itself succeeded — the failure is just the post-back. assert items[0].is_error is False assert _result_text(items[0]) == "result" assert len(events.send_calls) == 3 # used all 3 retries @pytest.mark.asyncio() async def test_yields_with_posted_false_on_permanent_4xx() -> None: """A permanent 4xx (e.g. 400) on send should short-circuit the retry loop after a single attempt and still yield with posted=False.""" async def echo(_input: dict[str, Any]) -> str: return "result" tool = _FakeTool("echo", echo) events = FakeAsyncEvents( stream_events=[_tool_use("tu_1", "echo", {}), _terminated()], send_failures=[_api_status_error(400)], ) items = [item async for item in _run_with_fakes(events=events, tools=[tool])] assert len(items) == 1 assert items[0].posted is False assert len(events.send_calls) == 1 # no retry on permanent 4xx # ---------- tool execution edge cases -------------------------------------- @pytest.mark.asyncio() async def test_tool_timeout(monkeypatch: pytest.MonkeyPatch) -> None: """Tool that exceeds ``TOOL_TIMEOUT`` yields with ``is_error=True`` and a ``"timed out"`` message — distinct from the generic exception path.""" monkeypatch.setattr(session_runner_mod, "TOOL_TIMEOUT", 0.05) async def slow(_input: dict[str, Any]) -> str: await asyncio.Event().wait() # never resolves return "never" tool = _FakeTool("slow", slow) events = FakeAsyncEvents(stream_events=[_tool_use("tu_1", "slow", {}), _terminated()]) items = [item async for item in _run_with_fakes(events=events, tools=[tool])] assert len(items) == 1 assert items[0].is_error is True assert "timed out" in _result_text(items[0]) def test_tool_timeout_exceeds_bash_default() -> None: """``TOOL_TIMEOUT`` MUST stay strictly greater than the bash tool's own ``BASH_DEFAULT_TIMEOUT``. If they were equal the outer per-tool-call ``fail_after`` could win the race against the bash tool's inner ``fail_after``; anyio would then raise a plain parent-scope ``Cancelled`` (not ``TimeoutError``), the bash tool's ``except TimeoutError`` subprocess cleanup would never run, and the next bash call would read the previous (timed-out) command's stale output. This test pins the invariant so the two constants can't silently converge. """ from anthropic.lib.tools.agent_toolset import BASH_DEFAULT_TIMEOUT assert session_runner_mod.TOOL_TIMEOUT > BASH_DEFAULT_TIMEOUT @pytest.mark.asyncio() async def test_tool_error_preserves_structured_content() -> None: """``ToolError`` raised by the tool preserves its structured content rather than being stringified through ``repr(e)``.""" structured = [{"type": "text", "text": "structured error"}] async def boom(_input: dict[str, Any]) -> str: raise ToolError(content=cast(Any, structured)) tool = _FakeTool("boom", boom) events = FakeAsyncEvents(stream_events=[_tool_use("tu_1", "boom", {}), _terminated()]) items = [item async for item in _run_with_fakes(events=events, tools=[tool])] assert len(items) == 1 assert items[0].is_error is True assert _result_content(items[0]) == structured # ---------- stream-loop edge cases ----------------------------------------- @pytest.mark.asyncio() async def test_stream_permanent_4xx_ends_iteration() -> None: """A permanent 4xx on the stream connect must not loop forever — the iterator should exit cleanly.""" events = FakeAsyncEvents(streams=[_api_status_error(401)]) items = [item async for item in _run_with_fakes(events=events, tools=[])] assert items == [] # Should not have retried after the permanent 4xx. assert events.stream_calls == 1 @pytest.mark.asyncio() async def test_reconcile_list_error_does_not_dispatch_partial() -> None: """If ``events.list`` errors mid-pagination, the partial ``pending`` list should not be enqueued — otherwise we'd risk re-running a tool whose result was on a page we never reached.""" counter = {"calls": 0} async def increment(_input: dict[str, Any]) -> str: counter["calls"] += 1 return "done" tool = _FakeTool("inc", increment) # The list yields a tool_use, then raises before we reach the result. # Without the fix this tool_use would be enqueued and re-executed. events = FakeAsyncEvents( list_events=[_tool_use("tu_1", "inc", {})], list_raises=_api_status_error(500), stream_events=[_terminated()], # no live tool_use either ) items = [item async for item in _run_with_fakes(events=events, tools=[tool])] assert items == [] assert counter["calls"] == 0 # ---------- environment-key auth ----------------------------------------- @pytest.mark.asyncio() async def test_environment_key_threads_through_to_scoped_client(scoped_calls: list[dict[str, Any]]) -> None: """When an environment key is set, the runner asks ``_scoped_client`` for a Bearer-only sub-client keyed to that environment. The actual header shape (``Authorization: Bearer …``, no ``X-Api-Key``, helper-telemetry on defaults) is the responsibility of ``_scoped_client`` itself — exercised separately in integration tests; here we just verify the runner threaded the right key.""" async def echo(_input: dict[str, Any]) -> str: return "ok" tool = _FakeTool("echo", echo) events = FakeAsyncEvents( list_events=[], stream_events=[_tool_use("tu_1", "echo", {}), _terminated()], ) [_ async for _ in _run_with_fakes(events=events, tools=[tool], environment_key="env_key")] assert scoped_calls == [{"environment_key": "env_key"}] @pytest.mark.asyncio() async def test_no_environment_key_threads_none_to_scoped_client(scoped_calls: list[dict[str, Any]]) -> None: """Without an environment key the runner still asks ``_scoped_client`` for a request client — passing ``None`` so the factory returns the parent client unchanged (just with a helper-telemetry header layered on).""" async def echo(_input: dict[str, Any]) -> str: return "ok" tool = _FakeTool("echo", echo) events = FakeAsyncEvents(stream_events=[_tool_use("tu_1", "echo", {}), _terminated()]) [_ async for _ in _run_with_fakes(events=events, tools=[tool])] assert scoped_calls == [{"environment_key": None}] @pytest.mark.asyncio() async def test_session_runner_threads_extra_headers_into_stream_list_and_send() -> None: """A caller-supplied ``extra_headers`` is threaded, unchanged, into every per-request call the runner makes: the event ``stream``, the history ``list``, and each result ``send``. The runner does no header munging — it just passes the caller's mapping to each call's ``extra_headers=``. Auth is handled by the scoped sub-client the runner builds from ``environment_key``, independent of this passthrough.""" async def echo(_input: dict[str, Any]) -> str: return "ok" tool = _FakeTool("echo", echo) events = FakeAsyncEvents( list_events=[], stream_events=[_tool_use("tu_1", "echo", {}), _terminated()], ) extras = {"x-trace-id": "abc123"} [ _ async for _ in _run_with_fakes( events=events, tools=[tool], environment_key="env_key", extra_headers=extras, ) ] assert events.stream_headers[0] == extras assert events.list_headers[0] == extras assert events.send_calls[0]["extra_headers"] == extras @pytest.mark.asyncio() @pytest.mark.skipif(PYDANTIC_V1, reason="tool functions are only supported with pydantic v2") async def test_runs_context_manager_tool_cleanup_on_exit() -> None: """A tool defined as an ``@asynccontextmanager`` via ``@beta_async_tool`` has its ``__aexit__`` driven by the runner cleanup path, additively to the legacy ``close`` hook.""" from contextlib import asynccontextmanager from anthropic.types.beta import BetaManagedAgentsAgentToolset20260401BashInput from anthropic.lib.tools._beta_functions import beta_async_tool events_seen: list[str] = [] @asynccontextmanager async def echo_cm() -> AsyncIterator[Callable[..., Awaitable[str]]]: events_seen.append("enter") # ``Optional[str]`` (not ``str | None``) because ``@beta_async_tool`` # evaluates these annotations at runtime via pydantic, and PEP 604 union # syntax can't be ``eval``'d under Python 3.9 — our minimum version. async def echo(command: Optional[str] = None) -> str: return f"echo:{command}" try: yield echo finally: events_seen.append("exit") echo_tool = beta_async_tool(name="echo", input_schema=BetaManagedAgentsAgentToolset20260401BashInput)( cast(Any, echo_cm) ) fake_events = FakeAsyncEvents(stream_events=[_tool_use("tu_1", "echo", {"command": "hi"}), _terminated()]) calls = [c async for c in _run_with_fakes(events=fake_events, tools=[echo_tool])] assert [_result_text(c) for c in calls] == ["echo:hi"] # Entered lazily on first dispatch, exited on runner cleanup. assert events_seen == ["enter", "exit"] # ---------- resource-method wrapper --------------------------------------- @pytest.mark.asyncio() async def test_tool_runner_method_returns_session_tool_runner() -> None: from anthropic import AsyncAnthropic client = AsyncAnthropic(api_key="dummy") runner = client.beta.sessions.events.tool_runner("s_1", tools=[]) assert isinstance(runner, SessionToolRunner) assert runner.session_id == "s_1" @pytest.mark.asyncio() async def test_until_done_drives_runner_to_completion() -> None: """``until_done()`` (renamed from ``run()`` to match ``BetaToolRunner`` and avoid colliding with ``EnvironmentWorker.run``'s forever-loop) drives the runner to the session end, discarding per-call observations.""" calls = {"n": 0} async def echo(_input: dict[str, Any]) -> str: calls["n"] += 1 return "ok" tool = _FakeTool("echo", echo) events = FakeAsyncEvents(stream_events=[_tool_use("tu_1", "echo", {}), _terminated()]) client = _FakeClient(events) runner = SessionToolRunner(cast(Any, client), "s_1", tools=cast(Any, [tool]), max_idle=None) # The old name is gone; the new one exists and returns at session end. assert not hasattr(runner, "run") await runner.until_done() assert calls["n"] == 1 assert len(events.send_calls) == 1 def test_environments_public_reexports() -> None: """A user can type their own code against the public runner API without reaching into a ``_``-private module.""" from anthropic.lib import environments as env_pkg for name in ( "BetaAnyRunnableTool", "DispatchedToolCall", "DispatchedToolUseEvent", "DispatchedToolResultParams", "download_session_skills", "SessionToolRunner", ): assert name in env_pkg.__all__, name assert getattr(env_pkg, name) is not None, name # The old ``RunnableTool`` name was renamed to ``BetaAnyRunnableTool``; it # must be fully gone (it had no released consumers). assert "RunnableTool" not in env_pkg.__all__ assert not hasattr(env_pkg, "RunnableTool") # ---------- _to_session_content --------------------------------------------- def _to_session_content(content: Any) -> list[Any]: return cast("list[Any]", session_runner_mod._to_session_content(content)) # pyright: ignore[reportPrivateUsage] def test_to_session_content_text_passthrough() -> None: out = _to_session_content([{"type": "text", "text": "hello"}]) assert out == [{"type": "text", "text": "hello"}] def test_to_session_content_image_passthrough() -> None: block = { "type": "image", "source": {"type": "base64", "media_type": "image/png", "data": "QUJD"}, } out = _to_session_content([block]) assert out == [block], "image blocks should pass through structurally, not be stringified" def test_to_session_content_document_passthrough() -> None: block = { "type": "document", "source": {"type": "url", "url": "https://example.com/doc.pdf"}, "title": "doc", } out = _to_session_content([block]) assert out == [block], "document blocks should pass through structurally, not be stringified" def test_to_session_content_search_result_passthrough() -> None: """A ``search_result`` block — valid on the Sessions content union — passes through structurally so the model retains the typed citation metadata.""" block = { "type": "search_result", "source": "https://example.com", "title": "result", "content": [{"type": "text", "text": "hit"}], "citations": {"enabled": True}, } out = _to_session_content([block]) assert out == [block], "search_result blocks should pass through structurally, not be stringified" def test_to_session_content_tool_reference_stringified() -> None: """``tool_reference`` blocks have no Sessions equivalent and must be stringified.""" block = {"type": "tool_reference", "tool_name": "weather"} out = _to_session_content([block]) assert out == [{"type": "text", "text": session_runner_mod.json.dumps(block)}] anthropic-sdk-python-0.120.2/tests/lib/tools/test_skills.py000066400000000000000000000156521523216435200237370ustar00rootroot00000000000000"""Tests for skill-archive extraction (:mod:`anthropic.lib.tools._skills`). Skill bundles are packaged wrapped in a single directory named after the skill (e.g. ``pdf/SKILL.md``). The extractor must strip that wrapper so files land at ``/SKILL.md``, not the doubled ``/pdf/SKILL.md``. It must also still refuse zip-slip / tar-slip members. """ from __future__ import annotations import io import os import stat import tarfile import zipfile from pathlib import Path from collections.abc import Callable ArchiveMaker = Callable[[Path, dict[str, bytes]], None] # Maps an entry name to ``(data, unix_mode)`` so a test can pin the mode the # archive records for that member. ArchiveModeMaker = Callable[[Path, "dict[str, tuple[bytes, int]]"], None] import pytest from anthropic.lib.tools._skills import _strip_top, _archive_top_dir, _extract_skill_archive def _make_zip(path: Path, entries: dict[str, bytes]) -> None: with zipfile.ZipFile(path, "w") as zf: for name, data in entries.items(): zf.writestr(name, data) def _make_targz(path: Path, entries: dict[str, bytes]) -> None: with tarfile.open(path, "w:gz") as tf: for name, data in entries.items(): info = tarfile.TarInfo(name) info.size = len(data) tf.addfile(info, io.BytesIO(data)) def _make_zip_modes(path: Path, entries: dict[str, tuple[bytes, int]]) -> None: with zipfile.ZipFile(path, "w") as zf: for name, (data, mode) in entries.items(): info = zipfile.ZipInfo(name) # The Unix mode lives in the high 16 bits of ``external_attr``. info.external_attr = mode << 16 zf.writestr(info, data) def _make_targz_modes(path: Path, entries: dict[str, tuple[bytes, int]]) -> None: with tarfile.open(path, "w:gz") as tf: for name, (data, mode) in entries.items(): info = tarfile.TarInfo(name) info.size = len(data) info.mode = mode tf.addfile(info, io.BytesIO(data)) def test_archive_top_dir_detection() -> None: assert _archive_top_dir(["pdf/SKILL.md", "pdf/scripts/x.py"]) == "pdf" assert _archive_top_dir(["pdf/SKILL.md"]) == "pdf" # No common single root -> no strip. assert _archive_top_dir(["SKILL.md", "scripts/x.py"]) == "" assert _archive_top_dir(["a/x", "b/y"]) == "" # Only the bare top dir, nothing nested -> nothing to unwrap. assert _archive_top_dir(["pdf/"]) == "" assert _archive_top_dir([]) == "" def test_strip_top() -> None: assert _strip_top("pdf/SKILL.md", "pdf") == "SKILL.md" assert _strip_top("pdf/scripts/x.py", "pdf") == "scripts/x.py" assert _strip_top("pdf", "pdf") == "" # bare top-dir entry assert _strip_top("SKILL.md", "") == "SKILL.md" # no wrapper -> unchanged assert _strip_top("other/x", "pdf") == "other/x" # not under the wrapper @pytest.mark.parametrize("make", [_make_zip, _make_targz]) def test_extract_strips_skill_wrapper_dir(make: ArchiveMaker, tmp_path: Path) -> None: archive = tmp_path / "skill.archive" make( archive, { "pdf/SKILL.md": b"# PDF", "pdf/scripts/run.py": b"print(1)", }, ) dest = tmp_path / "skills" / "pdf" _extract_skill_archive(archive, dest) # Stripped: files land directly under dest, not dest/pdf/. assert (dest / "SKILL.md").read_bytes() == b"# PDF" assert (dest / "scripts" / "run.py").read_bytes() == b"print(1)" assert not (dest / "pdf").exists(), "wrapper dir was not stripped (doubling)" @pytest.mark.parametrize("make", [_make_zip, _make_targz]) def test_extract_flat_archive_unchanged(make: ArchiveMaker, tmp_path: Path) -> None: archive = tmp_path / "skill.archive" make(archive, {"SKILL.md": b"# flat", "scripts/run.py": b"x"}) dest = tmp_path / "skills" / "flat" _extract_skill_archive(archive, dest) assert (dest / "SKILL.md").read_bytes() == b"# flat" assert (dest / "scripts" / "run.py").read_bytes() == b"x" def test_extract_refuses_zip_slip(tmp_path: Path) -> None: archive = tmp_path / "evil.zip" _make_zip(archive, {"../escape.txt": b"pwned"}) dest = tmp_path / "skills" / "x" with pytest.raises(ValueError): _extract_skill_archive(archive, dest) assert not (tmp_path / "skills" / "escape.txt").exists() assert not (tmp_path / "escape.txt").exists() def _mode(p: Path) -> int: return stat.S_IMODE(os.stat(p).st_mode) def test_zip_preserves_executable_bit(tmp_path: Path) -> None: archive = tmp_path / "skill.zip" _make_zip_modes( archive, { "scripts/run.sh": (b"#!/bin/sh\necho hi\n", 0o755), "SKILL.md": (b"# doc", 0o644), }, ) dest = tmp_path / "skills" / "x" _extract_skill_archive(archive, dest) exe = _mode(dest / "scripts" / "run.sh") doc = _mode(dest / "SKILL.md") assert exe & 0o111, "executable bit was dropped on zip extraction" assert exe == 0o755 assert doc & 0o111 == 0 assert doc == 0o644 def test_zip_without_unix_attrs_is_not_executable(tmp_path: Path) -> None: # ``writestr`` with a plain name records no Unix mode (external_attr == 0); # the member must extract non-executable rather than inherit a random mode. archive = tmp_path / "skill.zip" _make_zip(archive, {"SKILL.md": b"# doc", "scripts/run.sh": b"echo hi"}) dest = tmp_path / "skills" / "x" _extract_skill_archive(archive, dest) assert _mode(dest / "SKILL.md") == 0o644 assert _mode(dest / "scripts" / "run.sh") == 0o644 def test_tar_preserves_executable_bit(tmp_path: Path) -> None: archive = tmp_path / "skill.tar.gz" _make_targz_modes( archive, { "scripts/run.sh": (b"#!/bin/sh\necho hi\n", 0o755), "SKILL.md": (b"# doc", 0o644), }, ) dest = tmp_path / "skills" / "x" _extract_skill_archive(archive, dest) exe = _mode(dest / "scripts" / "run.sh") doc = _mode(dest / "SKILL.md") assert exe & 0o111, "executable bit was dropped on tar extraction" assert exe == 0o755 assert doc & 0o111 == 0 assert doc == 0o644 @pytest.mark.parametrize("make", [_make_zip_modes, _make_targz_modes]) def test_extract_drops_setuid_setgid_sticky(make: ArchiveModeMaker, tmp_path: Path) -> None: # setuid (0o4000) + setgid (0o2000) + sticky (0o1000) on an executable # member must never survive extraction; the mode collapses to plain 0o755. archive = tmp_path / "skill.archive" make( archive, { "scripts/run.sh": (b"#!/bin/sh\n", 0o7755), "SKILL.md": (b"# doc", 0o4644), }, ) dest = tmp_path / "skills" / "x" _extract_skill_archive(archive, dest) exe = _mode(dest / "scripts" / "run.sh") doc = _mode(dest / "SKILL.md") assert exe & 0o7000 == 0, "setuid/setgid/sticky leaked onto executable member" assert exe == 0o755 # A non-executable member with setuid set must also drop the bit. assert doc & 0o7000 == 0 assert doc == 0o644 anthropic-sdk-python-0.120.2/tests/lib/utils.py000066400000000000000000000051011523216435200213630ustar00rootroot00000000000000from __future__ import annotations import io import re import inspect from typing import Any, Iterable from typing_extensions import TypeAlias import rich import pytest import pydantic import rich.pretty import rich.console ReprArgs: TypeAlias = "Iterable[tuple[str | None, Any]]" def print_obj(obj: object) -> str: """Pretty print an object to a string""" # monkeypatch pydantic model printing so that model fields # are always printed in the same order so we can reliably # use this for snapshot tests original_repr = pydantic.BaseModel.__repr_args__ def __repr_args__(self: pydantic.BaseModel) -> ReprArgs: return sorted(original_repr(self), key=lambda arg: arg[0] or arg) def __repr_name__(self: pydantic.BaseModel) -> str: # Drop generic parameters from the name # e.g. `GenericModel[Location]` -> `GenericModel` return self.__class__.__name__.split("[", maxsplit=1)[0] with pytest.MonkeyPatch.context() as m: m.setattr(pydantic.BaseModel, "__repr_args__", __repr_args__) m.setattr(pydantic.BaseModel, "__repr_name__", __repr_name__) string = rich_print_str(obj) # we remove all `fn_name..` occurrences # so that we can share the same snapshots between # pydantic v1 and pydantic v2 as their output for # generic models differs, e.g. # # v2: `GenericModel[test_generic_model..Location]` # v1: `GenericModel[Location]` return clear_locals(string, stacklevel=2) def get_caller_name(*, stacklevel: int = 1) -> str: frame = inspect.currentframe() assert frame is not None for i in range(stacklevel): frame = frame.f_back assert frame is not None, f"no {i}th frame" return frame.f_code.co_name def clear_locals(string: str, *, stacklevel: int) -> str: caller = get_caller_name(stacklevel=stacklevel + 1) return string.replace(f"{caller}..", "") def rich_print_str(obj: object) -> str: """Like `rich.print()` but returns the string instead""" buf = io.StringIO() console = rich.console.Console( file=buf, width=120, force_terminal=False, color_system=None, record=True, ) # Use Rich's pretty printer for nice multi-line formatting rich.pretty.install(console) console.print(obj, overflow="fold", width=120) result = buf.getvalue() # Strip out [~...] patterns to exclude generic content from snapshots result = re.sub(r"\[~[^\]]*\]", "", result) result = result.replace("[TypeVar]", "") return result anthropic-sdk-python-0.120.2/tests/sample_file.txt000066400000000000000000000000161523216435200221240ustar00rootroot00000000000000Hello, world! anthropic-sdk-python-0.120.2/tests/test_client.py000066400000000000000000002764271523216435200220170ustar00rootroot00000000000000# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. from __future__ import annotations import gc import os import sys import json import asyncio import inspect import dataclasses import tracemalloc from typing import Any, Union, TypeVar, Callable, Iterable, Iterator, Optional, Coroutine, cast from unittest import mock from typing_extensions import Literal, AsyncIterator, override import httpx import pytest from respx import MockRouter from pydantic import ValidationError from anthropic import Anthropic, AsyncAnthropic, APIResponseValidationError from anthropic._types import Omit from anthropic._utils import asyncify from anthropic._models import BaseModel, FinalRequestOptions from anthropic._streaming import Stream, AsyncStream from anthropic._exceptions import APIStatusError, APITimeoutError, APIResponseValidationError from anthropic._base_client import ( DEFAULT_TIMEOUT, HTTPX_DEFAULT_TIMEOUT, BaseClient, OtherPlatform, DefaultHttpxClient, DefaultAsyncHttpxClient, get_platform, make_request_options, ) from .utils import update_env T = TypeVar("T") base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") api_key = "my-anthropic-api-key" def _get_params(client: BaseClient[Any, Any]) -> dict[str, str]: request = client._build_request(FinalRequestOptions(method="get", url="/foo")) url = httpx.URL(request.url) return dict(url.params) def _low_retry_timeout(*_args: Any, **_kwargs: Any) -> float: return 0.1 def mirror_request_content(request: httpx.Request) -> httpx.Response: return httpx.Response(200, content=request.content) # note: we can't use the httpx.MockTransport class as it consumes the request # body itself, which means we can't test that the body is read lazily class MockTransport(httpx.BaseTransport, httpx.AsyncBaseTransport): def __init__( self, handler: Callable[[httpx.Request], httpx.Response] | Callable[[httpx.Request], Coroutine[Any, Any, httpx.Response]], ) -> None: self.handler = handler @override def handle_request( self, request: httpx.Request, ) -> httpx.Response: assert not inspect.iscoroutinefunction(self.handler), "handler must not be a coroutine function" assert inspect.isfunction(self.handler), "handler must be a function" return self.handler(request) @override async def handle_async_request( self, request: httpx.Request, ) -> httpx.Response: assert inspect.iscoroutinefunction(self.handler), "handler must be a coroutine function" return await self.handler(request) @dataclasses.dataclass class Counter: value: int = 0 def _make_sync_iterator(iterable: Iterable[T], counter: Optional[Counter] = None) -> Iterator[T]: for item in iterable: if counter: counter.value += 1 yield item async def _make_async_iterator(iterable: Iterable[T], counter: Optional[Counter] = None) -> AsyncIterator[T]: for item in iterable: if counter: counter.value += 1 yield item def _get_open_connections(client: Anthropic | AsyncAnthropic) -> int: transport = client._client._transport assert isinstance(transport, httpx.HTTPTransport) or isinstance(transport, httpx.AsyncHTTPTransport) pool = transport._pool return len(pool._requests) @pytest.mark.parametrize("status_code", [400, 401, 403, 404, 409, 413, 422, 429, 500, 503, 529]) def test_make_status_error_sync_async_parity(status_code: int) -> None: # Anthropic._make_status_error and AsyncAnthropic._make_status_error are # separate manually-maintained copies; this guards against them drifting. sync_client = Anthropic(base_url=base_url, api_key=api_key, _strict_response_validation=True) async_client = AsyncAnthropic(base_url=base_url, api_key=api_key, _strict_response_validation=True) response = httpx.Response(status_code, request=httpx.Request("GET", "/")) sync_err = sync_client._make_status_error("msg", body=None, response=response) async_err = async_client._make_status_error("msg", body=None, response=response) assert type(sync_err) is type(async_err), ( f"sync returned {type(sync_err).__name__}, async returned {type(async_err).__name__}" ) class TestAnthropic: @pytest.mark.respx(base_url=base_url) def test_raw_response(self, respx_mock: MockRouter, client: Anthropic) -> None: respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) response = client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) assert response.json() == {"foo": "bar"} @pytest.mark.respx(base_url=base_url) def test_raw_response_for_binary(self, respx_mock: MockRouter, client: Anthropic) -> None: respx_mock.post("/foo").mock( return_value=httpx.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}') ) response = client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) assert response.json() == {"foo": "bar"} def test_copy(self, client: Anthropic) -> None: copied = client.copy() assert id(copied) != id(client) copied = client.copy(api_key="another my-anthropic-api-key") assert copied.api_key == "another my-anthropic-api-key" assert client.api_key == "my-anthropic-api-key" def test_copy_default_options(self, client: Anthropic) -> None: # options that have a default are overridden correctly copied = client.copy(max_retries=7) assert copied.max_retries == 7 assert client.max_retries == 2 copied2 = copied.copy(max_retries=6) assert copied2.max_retries == 6 assert copied.max_retries == 7 # timeout assert isinstance(client.timeout, httpx.Timeout) copied = client.copy(timeout=None) assert copied.timeout is None assert isinstance(client.timeout, httpx.Timeout) def test_copy_default_headers(self) -> None: client = Anthropic( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} ) assert client.default_headers["X-Foo"] == "bar" # does not override the already given value when not specified copied = client.copy() assert copied.default_headers["X-Foo"] == "bar" # merges already given headers copied = client.copy(default_headers={"X-Bar": "stainless"}) assert copied.default_headers["X-Foo"] == "bar" assert copied.default_headers["X-Bar"] == "stainless" # uses new values for any already given headers copied = client.copy(default_headers={"X-Foo": "stainless"}) assert copied.default_headers["X-Foo"] == "stainless" # set_default_headers # completely overrides already set values copied = client.copy(set_default_headers={}) assert copied.default_headers.get("X-Foo") is None copied = client.copy(set_default_headers={"X-Bar": "Robert"}) assert copied.default_headers["X-Bar"] == "Robert" with pytest.raises( ValueError, match="`default_headers` and `set_default_headers` arguments are mutually exclusive", ): client.copy(set_default_headers={}, default_headers={"X-Foo": "Bar"}) client.close() def test_copy_default_query(self) -> None: client = Anthropic( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"foo": "bar"} ) assert _get_params(client)["foo"] == "bar" # does not override the already given value when not specified copied = client.copy() assert _get_params(copied)["foo"] == "bar" # merges already given params copied = client.copy(default_query={"bar": "stainless"}) params = _get_params(copied) assert params["foo"] == "bar" assert params["bar"] == "stainless" # uses new values for any already given headers copied = client.copy(default_query={"foo": "stainless"}) assert _get_params(copied)["foo"] == "stainless" # set_default_query # completely overrides already set values copied = client.copy(set_default_query={}) assert _get_params(copied) == {} copied = client.copy(set_default_query={"bar": "Robert"}) assert _get_params(copied)["bar"] == "Robert" with pytest.raises( ValueError, # TODO: update match="`default_query` and `set_default_query` arguments are mutually exclusive", ): client.copy(set_default_query={}, default_query={"foo": "Bar"}) client.close() def test_copy_signature(self, client: Anthropic) -> None: # ensure the same parameters that can be passed to the client are defined in the `.copy()` method init_signature = inspect.signature( # mypy doesn't like that we access the `__init__` property. client.__init__, # type: ignore[misc] ) copy_signature = inspect.signature(client.copy) exclude_params = {"transport", "proxies", "_strict_response_validation", "_token_cache"} for name in init_signature.parameters.keys(): if name in exclude_params: continue copy_param = copy_signature.parameters.get(name) assert copy_param is not None, f"copy() signature is missing the {name} param" @pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12") def test_copy_build_request(self, client: Anthropic) -> None: options = FinalRequestOptions(method="get", url="/foo") def build_request(options: FinalRequestOptions) -> None: client_copy = client.copy() client_copy._build_request(options) # ensure that the machinery is warmed up before tracing starts. build_request(options) gc.collect() tracemalloc.start(1000) snapshot_before = tracemalloc.take_snapshot() ITERATIONS = 10 for _ in range(ITERATIONS): build_request(options) gc.collect() snapshot_after = tracemalloc.take_snapshot() tracemalloc.stop() def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.StatisticDiff) -> None: if diff.count == 0: # Avoid false positives by considering only leaks (i.e. allocations that persist). return if diff.count % ITERATIONS != 0: # Avoid false positives by considering only leaks that appear per iteration. return for frame in diff.traceback: if any( frame.filename.endswith(fragment) for fragment in [ # to_raw_response_wrapper leaks through the @functools.wraps() decorator. # # removing the decorator fixes the leak for reasons we don't understand. "anthropic/_legacy_response.py", "anthropic/_response.py", # pydantic.BaseModel.model_dump || pydantic.BaseModel.dict leak memory for some reason. "anthropic/_compat.py", # Standard library leaks we don't care about. "/logging/__init__.py", ] ): return leaks.append(diff) leaks: list[tracemalloc.StatisticDiff] = [] for diff in snapshot_after.compare_to(snapshot_before, "traceback"): add_leak(leaks, diff) if leaks: for leak in leaks: print("MEMORY LEAK:", leak) for frame in leak.traceback: print(frame) raise AssertionError() def test_request_timeout(self, client: Anthropic) -> None: request = client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT request = client._build_request(FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0))) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(100.0) def test_client_timeout_option(self) -> None: client = Anthropic( base_url=base_url, api_key=api_key, _strict_response_validation=True, timeout=httpx.Timeout(0) ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(0) client.close() def test_http_client_timeout_option(self) -> None: # custom timeout given to the httpx client should be used with httpx.Client(timeout=None) as http_client: client = Anthropic( base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(None) client.close() # no timeout given to the httpx client should not use the httpx default with httpx.Client() as http_client: client = Anthropic( base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT client.close() # explicitly passing the default timeout currently results in it being ignored with httpx.Client(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client: client = Anthropic( base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT # our default client.close() async def test_invalid_http_client(self) -> None: with pytest.raises(TypeError, match="Invalid `http_client` arg"): async with httpx.AsyncClient() as http_client: Anthropic( base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=cast(Any, http_client), ) def test_default_headers_option(self) -> None: test_client = Anthropic( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} ) request = test_client._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "bar" assert request.headers.get("x-stainless-lang") == "python" test_client2 = Anthropic( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={ "X-Foo": "stainless", "X-Stainless-Lang": "my-overriding-header", }, ) request = test_client2._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "stainless" assert request.headers.get("x-stainless-lang") == "my-overriding-header" test_client.close() test_client2.close() def test_validate_headers(self) -> None: client = Anthropic(base_url=base_url, api_key=api_key, _strict_response_validation=True) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("X-Api-Key") == api_key with mock.patch("anthropic._client.default_credentials", return_value=None): with update_env(**{"ANTHROPIC_API_KEY": Omit()}): client2 = Anthropic(base_url=base_url, api_key=None, _strict_response_validation=True) with pytest.raises( TypeError, match="Could not resolve authentication method. Expected one of api_key, auth_token, or credentials to be set. Or for one of the `X-Api-Key` or `Authorization` headers to be explicitly omitted", ): client2._build_request(FinalRequestOptions(method="get", url="/foo")) request2 = client2._build_request(FinalRequestOptions(method="get", url="/foo", headers={"X-Api-Key": Omit()})) assert request2.headers.get("X-Api-Key") is None def test_default_query_option(self) -> None: client = Anthropic( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"query_param": "bar"} ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) url = httpx.URL(request.url) assert dict(url.params) == {"query_param": "bar"} request = client._build_request( FinalRequestOptions( method="get", url="/foo", params={"foo": "baz", "query_param": "overridden"}, ) ) url = httpx.URL(request.url) assert dict(url.params) == {"foo": "baz", "query_param": "overridden"} client.close() def test_hardcoded_query_params_in_url(self, client: Anthropic) -> None: request = client._build_request(FinalRequestOptions(method="get", url="/foo?beta=true")) url = httpx.URL(request.url) assert dict(url.params) == {"beta": "true"} request = client._build_request( FinalRequestOptions( method="get", url="/foo?beta=true", params={"limit": "10", "page": "abc"}, ) ) url = httpx.URL(request.url) assert dict(url.params) == {"beta": "true", "limit": "10", "page": "abc"} request = client._build_request( FinalRequestOptions( method="get", url="/files/a%2Fb?beta=true", params={"limit": "10"}, ) ) assert request.url.raw_path == b"/files/a%2Fb?beta=true&limit=10" def test_request_extra_json(self, client: Anthropic) -> None: request = client._build_request( FinalRequestOptions( method="post", url="/foo", json_data={"foo": "bar"}, extra_json={"baz": False}, ), ) data = json.loads(request.content.decode("utf-8")) assert data == {"foo": "bar", "baz": False} request = client._build_request( FinalRequestOptions( method="post", url="/foo", extra_json={"baz": False}, ), ) data = json.loads(request.content.decode("utf-8")) assert data == {"baz": False} # `extra_json` takes priority over `json_data` when keys clash request = client._build_request( FinalRequestOptions( method="post", url="/foo", json_data={"foo": "bar", "baz": True}, extra_json={"baz": None}, ), ) data = json.loads(request.content.decode("utf-8")) assert data == {"foo": "bar", "baz": None} def test_request_extra_headers(self, client: Anthropic) -> None: request = client._build_request( FinalRequestOptions( method="post", url="/foo", **make_request_options(extra_headers={"X-Foo": "Foo"}), ), ) assert request.headers.get("X-Foo") == "Foo" # `extra_headers` takes priority over `default_headers` when keys clash request = client.with_options(default_headers={"X-Bar": "true"})._build_request( FinalRequestOptions( method="post", url="/foo", **make_request_options( extra_headers={"X-Bar": "false"}, ), ), ) assert request.headers.get("X-Bar") == "false" def test_request_extra_headers_httpx_headers(self, client: Anthropic) -> None: # `httpx.Headers` is accepted anywhere a header mapping is, in addition to a plain dict request = client.with_options(default_headers=httpx.Headers({"X-Bar": "true"}))._build_request( FinalRequestOptions( method="post", url="/foo", **make_request_options(extra_headers=httpx.Headers({"X-Foo": "Foo", "X-Bar": "false"})), ), ) assert request.headers.get("X-Foo") == "Foo" # `extra_headers` still takes priority over `default_headers` when keys clash assert request.headers.get("X-Bar") == "false" def test_request_x_stainless_helper_header_appends(self, client: Anthropic) -> None: # `x-stainless-helper` accumulates across mappings instead of being clobbered, # so a helper set on the client and one passed per-request both survive. request = client.with_options(default_headers={"x-stainless-helper": "session_runner"})._build_request( FinalRequestOptions( method="post", url="/foo", **make_request_options(extra_headers={"x-stainless-helper": "message_batches"}), ), ) assert request.headers.get("x-stainless-helper") == "session_runner, message_batches" def test_request_x_stainless_helper_header_dedupes(self, client: Anthropic) -> None: # the same helper set in both places is recorded once request = client.with_options(default_headers={"x-stainless-helper": "session_runner"})._build_request( FinalRequestOptions( method="post", url="/foo", **make_request_options(extra_headers={"x-stainless-helper": "session_runner"}), ), ) assert request.headers.get("x-stainless-helper") == "session_runner" def test_request_x_stainless_helper_header_collapses_case_variants(self, client: Anthropic) -> None: # differently-cased duplicates of the helper header fold into a single # deduplicated value instead of being sent as conflicting entries copied = client.with_options( default_headers={"X-Stainless-Helper": "parent", "x-stainless-helper": "scoped"}, ) request = copied._build_request( FinalRequestOptions( method="post", url="/foo", **make_request_options(extra_headers={"x-stainless-helper": "message_batches"}), ), ) assert request.headers.get("x-stainless-helper") == "parent, scoped, message_batches" def test_request_x_stainless_helper_header_dedupes_multi_value(self, client: Anthropic) -> None: # comma-separated values (e.g. several tagged tools) are deduplicated per token copied = client.with_options(default_headers={"x-stainless-helper": "session_runner, memory_tool"}) request = copied._build_request( FinalRequestOptions( method="post", url="/foo", **make_request_options(extra_headers={"x-stainless-helper": "memory_tool, message_batches"}), ), ) assert request.headers.get("x-stainless-helper") == "session_runner, memory_tool, message_batches" def test_copy_x_stainless_helper_header_appends(self, client: Anthropic) -> None: # stacking `default_headers` via copy()/with_options accumulates the # helper instead of clobbering, so e.g. a scoped sub-client's tag adds to # one already carried by the parent. copied = client.with_options(default_headers={"x-stainless-helper": "parent"}).with_options( default_headers={"x-stainless-helper": "child"} ) request = copied._build_request(FinalRequestOptions(method="post", url="/foo")) assert request.headers.get("x-stainless-helper") == "parent, child" def test_copy_preserves_header_removal(self, client: Anthropic) -> None: # an Omit removal set on the client still survives a subsequent copy() copied = client.with_options( default_headers=cast("dict[str, str]", {"X-Foo": Omit()}), ).with_options(default_headers={"X-Bar": "true"}) request = copied._build_request(FinalRequestOptions(method="post", url="/foo")) assert request.headers.get("X-Foo") is None assert request.headers.get("X-Bar") == "true" def test_request_extra_query(self, client: Anthropic) -> None: request = client._build_request( FinalRequestOptions( method="post", url="/foo", **make_request_options( extra_query={"my_query_param": "Foo"}, ), ), ) params = dict(request.url.params) assert params == {"my_query_param": "Foo"} # if both `query` and `extra_query` are given, they are merged request = client._build_request( FinalRequestOptions( method="post", url="/foo", **make_request_options( query={"bar": "1"}, extra_query={"foo": "2"}, ), ), ) params = dict(request.url.params) assert params == {"bar": "1", "foo": "2"} # `extra_query` takes priority over `query` when keys clash request = client._build_request( FinalRequestOptions( method="post", url="/foo", **make_request_options( query={"foo": "1"}, extra_query={"foo": "2"}, ), ), ) params = dict(request.url.params) assert params == {"foo": "2"} def test_multipart_repeating_array(self, client: Anthropic) -> None: request = client._build_request( FinalRequestOptions.construct( method="post", url="/foo", headers={"Content-Type": "multipart/form-data; boundary=6b7ba517decee4a450543ea6ae821c82"}, json_data={"array": ["foo", "bar"]}, files=[("foo.txt", b"hello world")], ) ) assert request.read().split(b"\r\n") == [ b"--6b7ba517decee4a450543ea6ae821c82", b'Content-Disposition: form-data; name="array[]"', b"", b"foo", b"--6b7ba517decee4a450543ea6ae821c82", b'Content-Disposition: form-data; name="array[]"', b"", b"bar", b"--6b7ba517decee4a450543ea6ae821c82", b'Content-Disposition: form-data; name="foo.txt"; filename="upload"', b"Content-Type: application/octet-stream", b"", b"hello world", b"--6b7ba517decee4a450543ea6ae821c82--", b"", ] @pytest.mark.respx(base_url=base_url) def test_binary_content_upload(self, respx_mock: MockRouter, client: Anthropic) -> None: respx_mock.post("/upload").mock(side_effect=mirror_request_content) file_content = b"Hello, this is a test file." response = client.post( "/upload", content=file_content, cast_to=httpx.Response, options={"headers": {"Content-Type": "application/octet-stream"}}, ) assert response.status_code == 200 assert response.request.headers["Content-Type"] == "application/octet-stream" assert response.content == file_content def test_binary_content_upload_with_iterator(self) -> None: file_content = b"Hello, this is a test file." counter = Counter() iterator = _make_sync_iterator([file_content], counter=counter) def mock_handler(request: httpx.Request) -> httpx.Response: assert counter.value == 0, "the request body should not have been read" return httpx.Response(200, content=request.read()) with Anthropic( base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=httpx.Client(transport=MockTransport(handler=mock_handler)), ) as client: response = client.post( "/upload", content=iterator, cast_to=httpx.Response, options={"headers": {"Content-Type": "application/octet-stream"}}, ) assert response.status_code == 200 assert response.request.headers["Content-Type"] == "application/octet-stream" assert response.content == file_content assert counter.value == 1 @pytest.mark.respx(base_url=base_url) def test_binary_content_upload_with_body_is_deprecated(self, respx_mock: MockRouter, client: Anthropic) -> None: respx_mock.post("/upload").mock(side_effect=mirror_request_content) file_content = b"Hello, this is a test file." with pytest.deprecated_call( match="Passing raw bytes as `body` is deprecated and will be removed in a future version. Please pass raw bytes via the `content` parameter instead." ): response = client.post( "/upload", body=file_content, cast_to=httpx.Response, options={"headers": {"Content-Type": "application/octet-stream"}}, ) assert response.status_code == 200 assert response.request.headers["Content-Type"] == "application/octet-stream" assert response.content == file_content @pytest.mark.respx(base_url=base_url) def test_basic_union_response(self, respx_mock: MockRouter, client: Anthropic) -> None: class Model1(BaseModel): name: str class Model2(BaseModel): foo: str respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model2) assert response.foo == "bar" @pytest.mark.respx(base_url=base_url) def test_union_response_different_types(self, respx_mock: MockRouter, client: Anthropic) -> None: """Union of objects with the same field name using a different type""" class Model1(BaseModel): foo: int class Model2(BaseModel): foo: str respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model2) assert response.foo == "bar" respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": 1})) response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model1) assert response.foo == 1 @pytest.mark.respx(base_url=base_url) def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter, client: Anthropic) -> None: """ Response that sets Content-Type to something other than application/json but returns json data """ class Model(BaseModel): foo: int respx_mock.get("/foo").mock( return_value=httpx.Response( 200, content=json.dumps({"foo": 2}), headers={"Content-Type": "application/text"}, ) ) response = client.get("/foo", cast_to=Model) assert isinstance(response, Model) assert response.foo == 2 def test_base_url_setter(self) -> None: client = Anthropic(base_url="https://example.com/from_init", api_key=api_key, _strict_response_validation=True) assert client.base_url == "https://example.com/from_init/" client.base_url = "https://example.com/from_setter" # type: ignore[assignment] assert client.base_url == "https://example.com/from_setter/" client.close() def test_base_url_env(self) -> None: with update_env(ANTHROPIC_BASE_URL="http://localhost:5000/from/env"): client = Anthropic(api_key=api_key, _strict_response_validation=True) assert client.base_url == "http://localhost:5000/from/env/" @pytest.mark.parametrize( "client", [ Anthropic(base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True), Anthropic( base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True, http_client=httpx.Client(), ), ], ids=["standard", "custom http client"], ) def test_base_url_trailing_slash(self, client: Anthropic) -> None: request = client._build_request( FinalRequestOptions( method="post", url="/foo", json_data={"foo": "bar"}, ), ) assert request.url == "http://localhost:5000/custom/path/foo" client.close() @pytest.mark.parametrize( "client", [ Anthropic(base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True), Anthropic( base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True, http_client=httpx.Client(), ), ], ids=["standard", "custom http client"], ) def test_base_url_no_trailing_slash(self, client: Anthropic) -> None: request = client._build_request( FinalRequestOptions( method="post", url="/foo", json_data={"foo": "bar"}, ), ) assert request.url == "http://localhost:5000/custom/path/foo" client.close() @pytest.mark.parametrize( "client", [ Anthropic(base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True), Anthropic( base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True, http_client=httpx.Client(), ), ], ids=["standard", "custom http client"], ) def test_absolute_request_url(self, client: Anthropic) -> None: request = client._build_request( FinalRequestOptions( method="post", url="https://myapi.com/foo", json_data={"foo": "bar"}, ), ) assert request.url == "https://myapi.com/foo" client.close() def test_copied_client_does_not_close_http(self) -> None: test_client = Anthropic(base_url=base_url, api_key=api_key, _strict_response_validation=True) assert not test_client.is_closed() copied = test_client.copy() assert copied is not test_client del copied assert not test_client.is_closed() def test_client_context_manager(self) -> None: test_client = Anthropic(base_url=base_url, api_key=api_key, _strict_response_validation=True) with test_client as c2: assert c2 is test_client assert not c2.is_closed() assert not test_client.is_closed() assert test_client.is_closed() @pytest.mark.respx(base_url=base_url) def test_client_response_validation_error(self, respx_mock: MockRouter, client: Anthropic) -> None: class Model(BaseModel): foo: str respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": {"invalid": True}})) with pytest.raises(APIResponseValidationError) as exc: client.get("/foo", cast_to=Model) assert isinstance(exc.value.__cause__, ValidationError) def test_client_max_retries_validation(self) -> None: with pytest.raises(TypeError, match=r"max_retries cannot be None"): Anthropic(base_url=base_url, api_key=api_key, _strict_response_validation=True, max_retries=cast(Any, None)) @pytest.mark.respx(base_url=base_url) def test_default_stream_cls(self, respx_mock: MockRouter, client: Anthropic) -> None: class Model(BaseModel): name: str respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) stream = client.post("/foo", cast_to=Model, stream=True, stream_cls=Stream[Model]) assert isinstance(stream, Stream) stream.response.close() @pytest.mark.respx(base_url=base_url) def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None: class Model(BaseModel): name: str respx_mock.get("/foo").mock(return_value=httpx.Response(200, text="my-custom-format")) strict_client = Anthropic(base_url=base_url, api_key=api_key, _strict_response_validation=True) with pytest.raises(APIResponseValidationError): strict_client.get("/foo", cast_to=Model) non_strict_client = Anthropic(base_url=base_url, api_key=api_key, _strict_response_validation=False) response = non_strict_client.get("/foo", cast_to=Model) assert isinstance(response, str) # type: ignore[unreachable] strict_client.close() non_strict_client.close() @pytest.mark.parametrize( "remaining_retries,retry_after,timeout", [ [3, "20", 20], [3, "0", 0.5], [3, "-10", 0.5], [3, "60", 60], [3, "61", 0.5], [3, "Fri, 29 Sep 2023 16:26:57 GMT", 20], [3, "Fri, 29 Sep 2023 16:26:37 GMT", 0.5], [3, "Fri, 29 Sep 2023 16:26:27 GMT", 0.5], [3, "Fri, 29 Sep 2023 16:27:37 GMT", 60], [3, "Fri, 29 Sep 2023 16:27:38 GMT", 0.5], [3, "99999999999999999999999999999999999", 0.5], [3, "Zun, 29 Sep 2023 16:26:27 GMT", 0.5], [3, "", 0.5], [2, "", 0.5 * 2.0], [1, "", 0.5 * 4.0], [-1100, "", 8], # test large number potentially overflowing ], ) @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) def test_parse_retry_after_header( self, remaining_retries: int, retry_after: str, timeout: float, client: Anthropic ) -> None: headers = httpx.Headers({"retry-after": retry_after}) options = FinalRequestOptions(method="get", url="/foo", max_retries=3) calculated = client._calculate_retry_timeout(remaining_retries, options, headers) assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType] @mock.patch("anthropic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, client: Anthropic) -> None: respx_mock.post("/v1/messages").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): client.messages.with_streaming_response.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ).__enter__() assert _get_open_connections(client) == 0 @mock.patch("anthropic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client: Anthropic) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): client.messages.with_streaming_response.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ).__enter__() assert _get_open_connections(client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("anthropic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) @pytest.mark.parametrize("failure_mode", ["status", "exception"]) def test_retries_taken( self, client: Anthropic, failures_before_success: int, failure_mode: Literal["status", "exception"], respx_mock: MockRouter, ) -> None: client = client.with_options(max_retries=4) nb_retries = 0 def retry_handler(_request: httpx.Request) -> httpx.Response: nonlocal nb_retries if nb_retries < failures_before_success: nb_retries += 1 if failure_mode == "exception": raise RuntimeError("oops") return httpx.Response(500) return httpx.Response(200) respx_mock.post("/v1/messages").mock(side_effect=retry_handler) response = client.messages.with_raw_response.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ) assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("anthropic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) def test_omit_retry_count_header( self, client: Anthropic, failures_before_success: int, respx_mock: MockRouter ) -> None: client = client.with_options(max_retries=4) nb_retries = 0 def retry_handler(_request: httpx.Request) -> httpx.Response: nonlocal nb_retries if nb_retries < failures_before_success: nb_retries += 1 return httpx.Response(500) return httpx.Response(200) respx_mock.post("/v1/messages").mock(side_effect=retry_handler) response = client.messages.with_raw_response.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", extra_headers={"x-stainless-retry-count": Omit()}, ) assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("anthropic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) def test_overwrite_retry_count_header( self, client: Anthropic, failures_before_success: int, respx_mock: MockRouter ) -> None: client = client.with_options(max_retries=4) nb_retries = 0 def retry_handler(_request: httpx.Request) -> httpx.Response: nonlocal nb_retries if nb_retries < failures_before_success: nb_retries += 1 return httpx.Response(500) return httpx.Response(200) respx_mock.post("/v1/messages").mock(side_effect=retry_handler) response = client.messages.with_raw_response.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", extra_headers={"x-stainless-retry-count": "42"}, ) assert response.http_request.headers.get("x-stainless-retry-count") == "42" @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("anthropic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) def test_retries_taken_new_response_class( self, client: Anthropic, failures_before_success: int, respx_mock: MockRouter ) -> None: client = client.with_options(max_retries=4) nb_retries = 0 def retry_handler(_request: httpx.Request) -> httpx.Response: nonlocal nb_retries if nb_retries < failures_before_success: nb_retries += 1 return httpx.Response(500) return httpx.Response(200) respx_mock.post("/v1/messages").mock(side_effect=retry_handler) with client.messages.with_streaming_response.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ) as response: assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None: # Test that the proxy environment variables are set correctly monkeypatch.setenv("HTTPS_PROXY", "https://example.org") # Delete in case our environment has any proxy env vars set monkeypatch.delenv("HTTP_PROXY", raising=False) monkeypatch.delenv("ALL_PROXY", raising=False) monkeypatch.delenv("NO_PROXY", raising=False) monkeypatch.delenv("http_proxy", raising=False) monkeypatch.delenv("https_proxy", raising=False) monkeypatch.delenv("all_proxy", raising=False) monkeypatch.delenv("no_proxy", raising=False) client = DefaultHttpxClient() mounts = tuple(client._mounts.items()) assert len(mounts) == 1 assert mounts[0][0].pattern == "https://" @pytest.mark.filterwarnings("ignore:.*deprecated.*:DeprecationWarning") def test_default_client_creation(self) -> None: # Ensure that the client can be initialized without any exceptions DefaultHttpxClient( verify=True, cert=None, trust_env=True, http1=True, http2=False, limits=httpx.Limits(max_connections=100, max_keepalive_connections=20), ) @pytest.mark.respx(base_url=base_url) def test_follow_redirects(self, respx_mock: MockRouter, client: Anthropic) -> None: # Test that the default follow_redirects=True allows following redirects respx_mock.post("/redirect").mock( return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) ) respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"})) response = client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response) assert response.status_code == 200 assert response.json() == {"status": "ok"} @pytest.mark.respx(base_url=base_url) def test_follow_redirects_disabled(self, respx_mock: MockRouter, client: Anthropic) -> None: # Test that follow_redirects=False prevents following redirects respx_mock.post("/redirect").mock( return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) ) with pytest.raises(APIStatusError) as exc_info: client.post("/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response) assert exc_info.value.response.status_code == 302 assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected" @pytest.mark.respx(base_url=base_url) def test_status_error_type_field(self, respx_mock: MockRouter, client: Anthropic) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response( 400, json={"type": "error", "error": {"type": "invalid_request_error", "message": "Bad request"}}, ) ) with pytest.raises(APIStatusError) as exc_info: client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert exc_info.value.type == "invalid_request_error" assert exc_info.value.status_code == 400 class TestAsyncAnthropic: @pytest.mark.respx(base_url=base_url) async def test_raw_response(self, respx_mock: MockRouter, async_client: AsyncAnthropic) -> None: respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) response = await async_client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) assert response.json() == {"foo": "bar"} @pytest.mark.respx(base_url=base_url) async def test_raw_response_for_binary(self, respx_mock: MockRouter, async_client: AsyncAnthropic) -> None: respx_mock.post("/foo").mock( return_value=httpx.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}') ) response = await async_client.post("/foo", cast_to=httpx.Response) assert response.status_code == 200 assert isinstance(response, httpx.Response) assert response.json() == {"foo": "bar"} def test_copy(self, async_client: AsyncAnthropic) -> None: copied = async_client.copy() assert id(copied) != id(async_client) copied = async_client.copy(api_key="another my-anthropic-api-key") assert copied.api_key == "another my-anthropic-api-key" assert async_client.api_key == "my-anthropic-api-key" def test_copy_default_options(self, async_client: AsyncAnthropic) -> None: # options that have a default are overridden correctly copied = async_client.copy(max_retries=7) assert copied.max_retries == 7 assert async_client.max_retries == 2 copied2 = copied.copy(max_retries=6) assert copied2.max_retries == 6 assert copied.max_retries == 7 # timeout assert isinstance(async_client.timeout, httpx.Timeout) copied = async_client.copy(timeout=None) assert copied.timeout is None assert isinstance(async_client.timeout, httpx.Timeout) async def test_copy_default_headers(self) -> None: client = AsyncAnthropic( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} ) assert client.default_headers["X-Foo"] == "bar" # does not override the already given value when not specified copied = client.copy() assert copied.default_headers["X-Foo"] == "bar" # merges already given headers copied = client.copy(default_headers={"X-Bar": "stainless"}) assert copied.default_headers["X-Foo"] == "bar" assert copied.default_headers["X-Bar"] == "stainless" # uses new values for any already given headers copied = client.copy(default_headers={"X-Foo": "stainless"}) assert copied.default_headers["X-Foo"] == "stainless" # set_default_headers # completely overrides already set values copied = client.copy(set_default_headers={}) assert copied.default_headers.get("X-Foo") is None copied = client.copy(set_default_headers={"X-Bar": "Robert"}) assert copied.default_headers["X-Bar"] == "Robert" with pytest.raises( ValueError, match="`default_headers` and `set_default_headers` arguments are mutually exclusive", ): client.copy(set_default_headers={}, default_headers={"X-Foo": "Bar"}) await client.close() async def test_copy_default_query(self) -> None: client = AsyncAnthropic( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"foo": "bar"} ) assert _get_params(client)["foo"] == "bar" # does not override the already given value when not specified copied = client.copy() assert _get_params(copied)["foo"] == "bar" # merges already given params copied = client.copy(default_query={"bar": "stainless"}) params = _get_params(copied) assert params["foo"] == "bar" assert params["bar"] == "stainless" # uses new values for any already given headers copied = client.copy(default_query={"foo": "stainless"}) assert _get_params(copied)["foo"] == "stainless" # set_default_query # completely overrides already set values copied = client.copy(set_default_query={}) assert _get_params(copied) == {} copied = client.copy(set_default_query={"bar": "Robert"}) assert _get_params(copied)["bar"] == "Robert" with pytest.raises( ValueError, # TODO: update match="`default_query` and `set_default_query` arguments are mutually exclusive", ): client.copy(set_default_query={}, default_query={"foo": "Bar"}) await client.close() def test_copy_signature(self, async_client: AsyncAnthropic) -> None: # ensure the same parameters that can be passed to the client are defined in the `.copy()` method init_signature = inspect.signature( # mypy doesn't like that we access the `__init__` property. async_client.__init__, # type: ignore[misc] ) copy_signature = inspect.signature(async_client.copy) exclude_params = {"transport", "proxies", "_strict_response_validation", "_token_cache"} for name in init_signature.parameters.keys(): if name in exclude_params: continue copy_param = copy_signature.parameters.get(name) assert copy_param is not None, f"copy() signature is missing the {name} param" @pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12") def test_copy_build_request(self, async_client: AsyncAnthropic) -> None: options = FinalRequestOptions(method="get", url="/foo") def build_request(options: FinalRequestOptions) -> None: client_copy = async_client.copy() client_copy._build_request(options) # ensure that the machinery is warmed up before tracing starts. build_request(options) gc.collect() tracemalloc.start(1000) snapshot_before = tracemalloc.take_snapshot() ITERATIONS = 10 for _ in range(ITERATIONS): build_request(options) gc.collect() snapshot_after = tracemalloc.take_snapshot() tracemalloc.stop() def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.StatisticDiff) -> None: if diff.count == 0: # Avoid false positives by considering only leaks (i.e. allocations that persist). return if diff.count % ITERATIONS != 0: # Avoid false positives by considering only leaks that appear per iteration. return for frame in diff.traceback: if any( frame.filename.endswith(fragment) for fragment in [ # to_raw_response_wrapper leaks through the @functools.wraps() decorator. # # removing the decorator fixes the leak for reasons we don't understand. "anthropic/_legacy_response.py", "anthropic/_response.py", # pydantic.BaseModel.model_dump || pydantic.BaseModel.dict leak memory for some reason. "anthropic/_compat.py", # Standard library leaks we don't care about. "/logging/__init__.py", ] ): return leaks.append(diff) leaks: list[tracemalloc.StatisticDiff] = [] for diff in snapshot_after.compare_to(snapshot_before, "traceback"): add_leak(leaks, diff) if leaks: for leak in leaks: print("MEMORY LEAK:", leak) for frame in leak.traceback: print(frame) raise AssertionError() async def test_request_timeout(self, async_client: AsyncAnthropic) -> None: request = async_client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT request = async_client._build_request( FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0)) ) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(100.0) async def test_client_timeout_option(self) -> None: client = AsyncAnthropic( base_url=base_url, api_key=api_key, _strict_response_validation=True, timeout=httpx.Timeout(0) ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(0) await client.close() async def test_http_client_timeout_option(self) -> None: # custom timeout given to the httpx client should be used async with httpx.AsyncClient(timeout=None) as http_client: client = AsyncAnthropic( base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == httpx.Timeout(None) await client.close() # no timeout given to the httpx client should not use the httpx default async with httpx.AsyncClient() as http_client: client = AsyncAnthropic( base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT await client.close() # explicitly passing the default timeout currently results in it being ignored async with httpx.AsyncClient(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client: client = AsyncAnthropic( base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=http_client ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore assert timeout == DEFAULT_TIMEOUT # our default await client.close() def test_invalid_http_client(self) -> None: with pytest.raises(TypeError, match="Invalid `http_client` arg"): with httpx.Client() as http_client: AsyncAnthropic( base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=cast(Any, http_client), ) async def test_default_headers_option(self) -> None: test_client = AsyncAnthropic( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"} ) request = test_client._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "bar" assert request.headers.get("x-stainless-lang") == "python" test_client2 = AsyncAnthropic( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={ "X-Foo": "stainless", "X-Stainless-Lang": "my-overriding-header", }, ) request = test_client2._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("x-foo") == "stainless" assert request.headers.get("x-stainless-lang") == "my-overriding-header" await test_client.close() await test_client2.close() def test_validate_headers(self) -> None: client = AsyncAnthropic(base_url=base_url, api_key=api_key, _strict_response_validation=True) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) assert request.headers.get("X-Api-Key") == api_key with mock.patch("anthropic._client.default_credentials", return_value=None): with update_env(**{"ANTHROPIC_API_KEY": Omit()}): client2 = AsyncAnthropic(base_url=base_url, api_key=None, _strict_response_validation=True) with pytest.raises( TypeError, match="Could not resolve authentication method. Expected one of api_key, auth_token, or credentials to be set. Or for one of the `X-Api-Key` or `Authorization` headers to be explicitly omitted", ): client2._build_request(FinalRequestOptions(method="get", url="/foo")) request2 = client2._build_request(FinalRequestOptions(method="get", url="/foo", headers={"X-Api-Key": Omit()})) assert request2.headers.get("X-Api-Key") is None async def test_default_query_option(self) -> None: client = AsyncAnthropic( base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"query_param": "bar"} ) request = client._build_request(FinalRequestOptions(method="get", url="/foo")) url = httpx.URL(request.url) assert dict(url.params) == {"query_param": "bar"} request = client._build_request( FinalRequestOptions( method="get", url="/foo", params={"foo": "baz", "query_param": "overridden"}, ) ) url = httpx.URL(request.url) assert dict(url.params) == {"foo": "baz", "query_param": "overridden"} await client.close() async def test_hardcoded_query_params_in_url(self, async_client: AsyncAnthropic) -> None: request = async_client._build_request(FinalRequestOptions(method="get", url="/foo?beta=true")) url = httpx.URL(request.url) assert dict(url.params) == {"beta": "true"} request = async_client._build_request( FinalRequestOptions( method="get", url="/foo?beta=true", params={"limit": "10", "page": "abc"}, ) ) url = httpx.URL(request.url) assert dict(url.params) == {"beta": "true", "limit": "10", "page": "abc"} request = async_client._build_request( FinalRequestOptions( method="get", url="/files/a%2Fb?beta=true", params={"limit": "10"}, ) ) assert request.url.raw_path == b"/files/a%2Fb?beta=true&limit=10" def test_request_extra_json(self, client: Anthropic) -> None: request = client._build_request( FinalRequestOptions( method="post", url="/foo", json_data={"foo": "bar"}, extra_json={"baz": False}, ), ) data = json.loads(request.content.decode("utf-8")) assert data == {"foo": "bar", "baz": False} request = client._build_request( FinalRequestOptions( method="post", url="/foo", extra_json={"baz": False}, ), ) data = json.loads(request.content.decode("utf-8")) assert data == {"baz": False} # `extra_json` takes priority over `json_data` when keys clash request = client._build_request( FinalRequestOptions( method="post", url="/foo", json_data={"foo": "bar", "baz": True}, extra_json={"baz": None}, ), ) data = json.loads(request.content.decode("utf-8")) assert data == {"foo": "bar", "baz": None} def test_request_extra_headers(self, client: Anthropic) -> None: request = client._build_request( FinalRequestOptions( method="post", url="/foo", **make_request_options(extra_headers={"X-Foo": "Foo"}), ), ) assert request.headers.get("X-Foo") == "Foo" # `extra_headers` takes priority over `default_headers` when keys clash request = client.with_options(default_headers={"X-Bar": "true"})._build_request( FinalRequestOptions( method="post", url="/foo", **make_request_options( extra_headers={"X-Bar": "false"}, ), ), ) assert request.headers.get("X-Bar") == "false" def test_request_extra_headers_httpx_headers(self, async_client: AsyncAnthropic) -> None: # `httpx.Headers` is accepted anywhere a header mapping is, in addition to a plain dict request = async_client.with_options(default_headers=httpx.Headers({"X-Bar": "true"}))._build_request( FinalRequestOptions( method="post", url="/foo", **make_request_options(extra_headers=httpx.Headers({"X-Foo": "Foo", "X-Bar": "false"})), ), ) assert request.headers.get("X-Foo") == "Foo" # `extra_headers` still takes priority over `default_headers` when keys clash assert request.headers.get("X-Bar") == "false" def test_request_x_stainless_helper_header_appends(self, async_client: AsyncAnthropic) -> None: # `x-stainless-helper` accumulates across mappings instead of being clobbered, # so a helper set on the client and one passed per-request both survive. request = async_client.with_options(default_headers={"x-stainless-helper": "session_runner"})._build_request( FinalRequestOptions( method="post", url="/foo", **make_request_options(extra_headers={"x-stainless-helper": "message_batches"}), ), ) assert request.headers.get("x-stainless-helper") == "session_runner, message_batches" def test_request_x_stainless_helper_header_dedupes(self, async_client: AsyncAnthropic) -> None: # the same helper set in both places is recorded once request = async_client.with_options(default_headers={"x-stainless-helper": "session_runner"})._build_request( FinalRequestOptions( method="post", url="/foo", **make_request_options(extra_headers={"x-stainless-helper": "session_runner"}), ), ) assert request.headers.get("x-stainless-helper") == "session_runner" def test_request_x_stainless_helper_header_collapses_case_variants(self, async_client: AsyncAnthropic) -> None: # differently-cased duplicates of the helper header fold into a single # deduplicated value instead of being sent as conflicting entries copied = async_client.with_options( default_headers={"X-Stainless-Helper": "parent", "x-stainless-helper": "scoped"}, ) request = copied._build_request( FinalRequestOptions( method="post", url="/foo", **make_request_options(extra_headers={"x-stainless-helper": "message_batches"}), ), ) assert request.headers.get("x-stainless-helper") == "parent, scoped, message_batches" def test_request_x_stainless_helper_header_dedupes_multi_value(self, async_client: AsyncAnthropic) -> None: # comma-separated values (e.g. several tagged tools) are deduplicated per token copied = async_client.with_options(default_headers={"x-stainless-helper": "session_runner, memory_tool"}) request = copied._build_request( FinalRequestOptions( method="post", url="/foo", **make_request_options(extra_headers={"x-stainless-helper": "memory_tool, message_batches"}), ), ) assert request.headers.get("x-stainless-helper") == "session_runner, memory_tool, message_batches" def test_copy_x_stainless_helper_header_appends(self, async_client: AsyncAnthropic) -> None: # stacking `default_headers` via copy()/with_options accumulates the # helper instead of clobbering, so e.g. a scoped sub-client's tag adds to # one already carried by the parent. copied = async_client.with_options(default_headers={"x-stainless-helper": "parent"}).with_options( default_headers={"x-stainless-helper": "child"} ) request = copied._build_request(FinalRequestOptions(method="post", url="/foo")) assert request.headers.get("x-stainless-helper") == "parent, child" def test_copy_preserves_header_removal(self, async_client: AsyncAnthropic) -> None: # an Omit removal set on the client still survives a subsequent copy() copied = async_client.with_options( default_headers=cast("dict[str, str]", {"X-Foo": Omit()}), ).with_options(default_headers={"X-Bar": "true"}) request = copied._build_request(FinalRequestOptions(method="post", url="/foo")) assert request.headers.get("X-Foo") is None assert request.headers.get("X-Bar") == "true" def test_request_extra_query(self, client: Anthropic) -> None: request = client._build_request( FinalRequestOptions( method="post", url="/foo", **make_request_options( extra_query={"my_query_param": "Foo"}, ), ), ) params = dict(request.url.params) assert params == {"my_query_param": "Foo"} # if both `query` and `extra_query` are given, they are merged request = client._build_request( FinalRequestOptions( method="post", url="/foo", **make_request_options( query={"bar": "1"}, extra_query={"foo": "2"}, ), ), ) params = dict(request.url.params) assert params == {"bar": "1", "foo": "2"} # `extra_query` takes priority over `query` when keys clash request = client._build_request( FinalRequestOptions( method="post", url="/foo", **make_request_options( query={"foo": "1"}, extra_query={"foo": "2"}, ), ), ) params = dict(request.url.params) assert params == {"foo": "2"} def test_multipart_repeating_array(self, async_client: AsyncAnthropic) -> None: request = async_client._build_request( FinalRequestOptions.construct( method="post", url="/foo", headers={"Content-Type": "multipart/form-data; boundary=6b7ba517decee4a450543ea6ae821c82"}, json_data={"array": ["foo", "bar"]}, files=[("foo.txt", b"hello world")], ) ) assert request.read().split(b"\r\n") == [ b"--6b7ba517decee4a450543ea6ae821c82", b'Content-Disposition: form-data; name="array[]"', b"", b"foo", b"--6b7ba517decee4a450543ea6ae821c82", b'Content-Disposition: form-data; name="array[]"', b"", b"bar", b"--6b7ba517decee4a450543ea6ae821c82", b'Content-Disposition: form-data; name="foo.txt"; filename="upload"', b"Content-Type: application/octet-stream", b"", b"hello world", b"--6b7ba517decee4a450543ea6ae821c82--", b"", ] @pytest.mark.respx(base_url=base_url) async def test_binary_content_upload(self, respx_mock: MockRouter, async_client: AsyncAnthropic) -> None: respx_mock.post("/upload").mock(side_effect=mirror_request_content) file_content = b"Hello, this is a test file." response = await async_client.post( "/upload", content=file_content, cast_to=httpx.Response, options={"headers": {"Content-Type": "application/octet-stream"}}, ) assert response.status_code == 200 assert response.request.headers["Content-Type"] == "application/octet-stream" assert response.content == file_content async def test_binary_content_upload_with_asynciterator(self) -> None: file_content = b"Hello, this is a test file." counter = Counter() iterator = _make_async_iterator([file_content], counter=counter) async def mock_handler(request: httpx.Request) -> httpx.Response: assert counter.value == 0, "the request body should not have been read" return httpx.Response(200, content=await request.aread()) async with AsyncAnthropic( base_url=base_url, api_key=api_key, _strict_response_validation=True, http_client=httpx.AsyncClient(transport=MockTransport(handler=mock_handler)), ) as client: response = await client.post( "/upload", content=iterator, cast_to=httpx.Response, options={"headers": {"Content-Type": "application/octet-stream"}}, ) assert response.status_code == 200 assert response.request.headers["Content-Type"] == "application/octet-stream" assert response.content == file_content assert counter.value == 1 @pytest.mark.respx(base_url=base_url) async def test_binary_content_upload_with_body_is_deprecated( self, respx_mock: MockRouter, async_client: AsyncAnthropic ) -> None: respx_mock.post("/upload").mock(side_effect=mirror_request_content) file_content = b"Hello, this is a test file." with pytest.deprecated_call( match="Passing raw bytes as `body` is deprecated and will be removed in a future version. Please pass raw bytes via the `content` parameter instead." ): response = await async_client.post( "/upload", body=file_content, cast_to=httpx.Response, options={"headers": {"Content-Type": "application/octet-stream"}}, ) assert response.status_code == 200 assert response.request.headers["Content-Type"] == "application/octet-stream" assert response.content == file_content @pytest.mark.respx(base_url=base_url) async def test_basic_union_response(self, respx_mock: MockRouter, async_client: AsyncAnthropic) -> None: class Model1(BaseModel): name: str class Model2(BaseModel): foo: str respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model2) assert response.foo == "bar" @pytest.mark.respx(base_url=base_url) async def test_union_response_different_types(self, respx_mock: MockRouter, async_client: AsyncAnthropic) -> None: """Union of objects with the same field name using a different type""" class Model1(BaseModel): foo: int class Model2(BaseModel): foo: str respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model2) assert response.foo == "bar" respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": 1})) response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2])) assert isinstance(response, Model1) assert response.foo == 1 @pytest.mark.respx(base_url=base_url) async def test_non_application_json_content_type_for_json_data( self, respx_mock: MockRouter, async_client: AsyncAnthropic ) -> None: """ Response that sets Content-Type to something other than application/json but returns json data """ class Model(BaseModel): foo: int respx_mock.get("/foo").mock( return_value=httpx.Response( 200, content=json.dumps({"foo": 2}), headers={"Content-Type": "application/text"}, ) ) response = await async_client.get("/foo", cast_to=Model) assert isinstance(response, Model) assert response.foo == 2 async def test_base_url_setter(self) -> None: client = AsyncAnthropic( base_url="https://example.com/from_init", api_key=api_key, _strict_response_validation=True ) assert client.base_url == "https://example.com/from_init/" client.base_url = "https://example.com/from_setter" # type: ignore[assignment] assert client.base_url == "https://example.com/from_setter/" await client.close() async def test_base_url_env(self) -> None: with update_env(ANTHROPIC_BASE_URL="http://localhost:5000/from/env"): client = AsyncAnthropic(api_key=api_key, _strict_response_validation=True) assert client.base_url == "http://localhost:5000/from/env/" @pytest.mark.parametrize( "client", [ AsyncAnthropic( base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True ), AsyncAnthropic( base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True, http_client=httpx.AsyncClient(), ), ], ids=["standard", "custom http client"], ) async def test_base_url_trailing_slash(self, client: AsyncAnthropic) -> None: request = client._build_request( FinalRequestOptions( method="post", url="/foo", json_data={"foo": "bar"}, ), ) assert request.url == "http://localhost:5000/custom/path/foo" await client.close() @pytest.mark.parametrize( "client", [ AsyncAnthropic( base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True ), AsyncAnthropic( base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True, http_client=httpx.AsyncClient(), ), ], ids=["standard", "custom http client"], ) async def test_base_url_no_trailing_slash(self, client: AsyncAnthropic) -> None: request = client._build_request( FinalRequestOptions( method="post", url="/foo", json_data={"foo": "bar"}, ), ) assert request.url == "http://localhost:5000/custom/path/foo" await client.close() @pytest.mark.parametrize( "client", [ AsyncAnthropic( base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True ), AsyncAnthropic( base_url="http://localhost:5000/custom/path/", api_key=api_key, _strict_response_validation=True, http_client=httpx.AsyncClient(), ), ], ids=["standard", "custom http client"], ) async def test_absolute_request_url(self, client: AsyncAnthropic) -> None: request = client._build_request( FinalRequestOptions( method="post", url="https://myapi.com/foo", json_data={"foo": "bar"}, ), ) assert request.url == "https://myapi.com/foo" await client.close() async def test_copied_client_does_not_close_http(self) -> None: test_client = AsyncAnthropic(base_url=base_url, api_key=api_key, _strict_response_validation=True) assert not test_client.is_closed() copied = test_client.copy() assert copied is not test_client del copied await asyncio.sleep(0.2) assert not test_client.is_closed() async def test_client_context_manager(self) -> None: test_client = AsyncAnthropic(base_url=base_url, api_key=api_key, _strict_response_validation=True) async with test_client as c2: assert c2 is test_client assert not c2.is_closed() assert not test_client.is_closed() assert test_client.is_closed() @pytest.mark.respx(base_url=base_url) async def test_client_response_validation_error(self, respx_mock: MockRouter, async_client: AsyncAnthropic) -> None: class Model(BaseModel): foo: str respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": {"invalid": True}})) with pytest.raises(APIResponseValidationError) as exc: await async_client.get("/foo", cast_to=Model) assert isinstance(exc.value.__cause__, ValidationError) async def test_client_max_retries_validation(self) -> None: with pytest.raises(TypeError, match=r"max_retries cannot be None"): AsyncAnthropic( base_url=base_url, api_key=api_key, _strict_response_validation=True, max_retries=cast(Any, None) ) @pytest.mark.respx(base_url=base_url) async def test_default_stream_cls(self, respx_mock: MockRouter, async_client: AsyncAnthropic) -> None: class Model(BaseModel): name: str respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"})) stream = await async_client.post("/foo", cast_to=Model, stream=True, stream_cls=AsyncStream[Model]) assert isinstance(stream, AsyncStream) await stream.response.aclose() @pytest.mark.respx(base_url=base_url) async def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None: class Model(BaseModel): name: str respx_mock.get("/foo").mock(return_value=httpx.Response(200, text="my-custom-format")) strict_client = AsyncAnthropic(base_url=base_url, api_key=api_key, _strict_response_validation=True) with pytest.raises(APIResponseValidationError): await strict_client.get("/foo", cast_to=Model) non_strict_client = AsyncAnthropic(base_url=base_url, api_key=api_key, _strict_response_validation=False) response = await non_strict_client.get("/foo", cast_to=Model) assert isinstance(response, str) # type: ignore[unreachable] await strict_client.close() await non_strict_client.close() @pytest.mark.parametrize( "remaining_retries,retry_after,timeout", [ [3, "20", 20], [3, "0", 0.5], [3, "-10", 0.5], [3, "60", 60], [3, "61", 0.5], [3, "Fri, 29 Sep 2023 16:26:57 GMT", 20], [3, "Fri, 29 Sep 2023 16:26:37 GMT", 0.5], [3, "Fri, 29 Sep 2023 16:26:27 GMT", 0.5], [3, "Fri, 29 Sep 2023 16:27:37 GMT", 60], [3, "Fri, 29 Sep 2023 16:27:38 GMT", 0.5], [3, "99999999999999999999999999999999999", 0.5], [3, "Zun, 29 Sep 2023 16:26:27 GMT", 0.5], [3, "", 0.5], [2, "", 0.5 * 2.0], [1, "", 0.5 * 4.0], [-1100, "", 8], # test large number potentially overflowing ], ) @mock.patch("time.time", mock.MagicMock(return_value=1696004797)) async def test_parse_retry_after_header( self, remaining_retries: int, retry_after: str, timeout: float, async_client: AsyncAnthropic ) -> None: headers = httpx.Headers({"retry-after": retry_after}) options = FinalRequestOptions(method="get", url="/foo", max_retries=3) calculated = async_client._calculate_retry_timeout(remaining_retries, options, headers) assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType] @mock.patch("anthropic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) async def test_retrying_timeout_errors_doesnt_leak( self, respx_mock: MockRouter, async_client: AsyncAnthropic ) -> None: respx_mock.post("/v1/messages").mock(side_effect=httpx.TimeoutException("Test timeout error")) with pytest.raises(APITimeoutError): await async_client.messages.with_streaming_response.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ).__aenter__() assert _get_open_connections(async_client) == 0 @mock.patch("anthropic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) async def test_retrying_status_errors_doesnt_leak( self, respx_mock: MockRouter, async_client: AsyncAnthropic ) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(500)) with pytest.raises(APIStatusError): await async_client.messages.with_streaming_response.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ).__aenter__() assert _get_open_connections(async_client) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("anthropic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) @pytest.mark.parametrize("failure_mode", ["status", "exception"]) async def test_retries_taken( self, async_client: AsyncAnthropic, failures_before_success: int, failure_mode: Literal["status", "exception"], respx_mock: MockRouter, ) -> None: client = async_client.with_options(max_retries=4) nb_retries = 0 def retry_handler(_request: httpx.Request) -> httpx.Response: nonlocal nb_retries if nb_retries < failures_before_success: nb_retries += 1 if failure_mode == "exception": raise RuntimeError("oops") return httpx.Response(500) return httpx.Response(200) respx_mock.post("/v1/messages").mock(side_effect=retry_handler) response = await client.messages.with_raw_response.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ) assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("anthropic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) async def test_omit_retry_count_header( self, async_client: AsyncAnthropic, failures_before_success: int, respx_mock: MockRouter ) -> None: client = async_client.with_options(max_retries=4) nb_retries = 0 def retry_handler(_request: httpx.Request) -> httpx.Response: nonlocal nb_retries if nb_retries < failures_before_success: nb_retries += 1 return httpx.Response(500) return httpx.Response(200) respx_mock.post("/v1/messages").mock(side_effect=retry_handler) response = await client.messages.with_raw_response.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", extra_headers={"x-stainless-retry-count": Omit()}, ) assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0 @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("anthropic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) async def test_overwrite_retry_count_header( self, async_client: AsyncAnthropic, failures_before_success: int, respx_mock: MockRouter ) -> None: client = async_client.with_options(max_retries=4) nb_retries = 0 def retry_handler(_request: httpx.Request) -> httpx.Response: nonlocal nb_retries if nb_retries < failures_before_success: nb_retries += 1 return httpx.Response(500) return httpx.Response(200) respx_mock.post("/v1/messages").mock(side_effect=retry_handler) response = await client.messages.with_raw_response.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", extra_headers={"x-stainless-retry-count": "42"}, ) assert response.http_request.headers.get("x-stainless-retry-count") == "42" @pytest.mark.parametrize("failures_before_success", [0, 2, 4]) @mock.patch("anthropic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) async def test_retries_taken_new_response_class( self, async_client: AsyncAnthropic, failures_before_success: int, respx_mock: MockRouter ) -> None: client = async_client.with_options(max_retries=4) nb_retries = 0 def retry_handler(_request: httpx.Request) -> httpx.Response: nonlocal nb_retries if nb_retries < failures_before_success: nb_retries += 1 return httpx.Response(500) return httpx.Response(200) respx_mock.post("/v1/messages").mock(side_effect=retry_handler) async with client.messages.with_streaming_response.create( max_tokens=1024, messages=[ { "content": "Hello, world", "role": "user", } ], model="claude-opus-4-6", ) as response: assert response.retries_taken == failures_before_success assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success async def test_get_platform(self) -> None: platform = await asyncify(get_platform)() assert isinstance(platform, (str, OtherPlatform)) async def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None: # Test that the proxy environment variables are set correctly monkeypatch.setenv("HTTPS_PROXY", "https://example.org") # Delete in case our environment has any proxy env vars set monkeypatch.delenv("HTTP_PROXY", raising=False) monkeypatch.delenv("ALL_PROXY", raising=False) monkeypatch.delenv("NO_PROXY", raising=False) monkeypatch.delenv("http_proxy", raising=False) monkeypatch.delenv("https_proxy", raising=False) monkeypatch.delenv("all_proxy", raising=False) monkeypatch.delenv("no_proxy", raising=False) client = DefaultAsyncHttpxClient() mounts = tuple(client._mounts.items()) assert len(mounts) == 1 assert mounts[0][0].pattern == "https://" @pytest.mark.filterwarnings("ignore:.*deprecated.*:DeprecationWarning") async def test_default_client_creation(self) -> None: # Ensure that the client can be initialized without any exceptions DefaultAsyncHttpxClient( verify=True, cert=None, trust_env=True, http1=True, http2=False, limits=httpx.Limits(max_connections=100, max_keepalive_connections=20), ) @pytest.mark.respx(base_url=base_url) async def test_follow_redirects(self, respx_mock: MockRouter, async_client: AsyncAnthropic) -> None: # Test that the default follow_redirects=True allows following redirects respx_mock.post("/redirect").mock( return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) ) respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"})) response = await async_client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response) assert response.status_code == 200 assert response.json() == {"status": "ok"} @pytest.mark.respx(base_url=base_url) async def test_follow_redirects_disabled(self, respx_mock: MockRouter, async_client: AsyncAnthropic) -> None: # Test that follow_redirects=False prevents following redirects respx_mock.post("/redirect").mock( return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"}) ) with pytest.raises(APIStatusError) as exc_info: await async_client.post( "/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response ) assert exc_info.value.response.status_code == 302 assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected" @pytest.mark.respx(base_url=base_url) async def test_status_error_type_field(self, respx_mock: MockRouter, async_client: AsyncAnthropic) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response( 400, json={"type": "error", "error": {"type": "invalid_request_error", "message": "Bad request"}}, ) ) with pytest.raises(APIStatusError) as exc_info: await async_client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert exc_info.value.type == "invalid_request_error" assert exc_info.value.status_code == 400 anthropic-sdk-python-0.120.2/tests/test_extract_files.py000066400000000000000000000063261523216435200233620ustar00rootroot00000000000000from __future__ import annotations from typing import Sequence import pytest from anthropic._types import FileTypes, ArrayFormat from anthropic._utils import extract_files def test_removes_files_from_input() -> None: query = {"foo": "bar"} assert extract_files(query, paths=[]) == [] assert query == {"foo": "bar"} query2 = {"foo": b"Bar", "hello": "world"} assert extract_files(query2, paths=[["foo"]]) == [("foo", b"Bar")] assert query2 == {"hello": "world"} query3 = {"foo": {"foo": {"bar": b"Bar"}}, "hello": "world"} assert extract_files(query3, paths=[["foo", "foo", "bar"]]) == [("foo[foo][bar]", b"Bar")] assert query3 == {"foo": {"foo": {}}, "hello": "world"} query4 = {"foo": {"bar": b"Bar", "baz": "foo"}, "hello": "world"} assert extract_files(query4, paths=[["foo", "bar"]]) == [("foo[bar]", b"Bar")] assert query4 == {"hello": "world", "foo": {"baz": "foo"}} def test_multiple_files() -> None: query = {"documents": [{"file": b"My first file"}, {"file": b"My second file"}]} assert extract_files(query, paths=[["documents", "", "file"]]) == [ ("documents[][file]", b"My first file"), ("documents[][file]", b"My second file"), ] assert query == {"documents": [{}, {}]} def test_top_level_file_array() -> None: query = {"files": [b"file one", b"file two"], "title": "hello"} assert extract_files(query, paths=[["files", ""]]) == [("files[]", b"file one"), ("files[]", b"file two")] assert query == {"title": "hello"} @pytest.mark.parametrize( "query,paths,expected", [ [ {"foo": {"bar": "baz"}}, [["foo", "", "bar"]], [], ], [ {"foo": ["bar", "baz"]}, [["foo", "bar"]], [], ], [ {"foo": {"bar": "baz"}}, [["foo", "foo"]], [], ], ], ids=["dict expecting array", "array expecting dict", "unknown keys"], ) def test_ignores_incorrect_paths( query: dict[str, object], paths: Sequence[Sequence[str]], expected: list[tuple[str, FileTypes]], ) -> None: assert extract_files(query, paths=paths) == expected @pytest.mark.parametrize( "array_format,expected_top_level,expected_nested", [ ("brackets", [("files[]", b"a"), ("files[]", b"b")], [("items[][file]", b"a"), ("items[][file]", b"b")]), ("repeat", [("files", b"a"), ("files", b"b")], [("items[file]", b"a"), ("items[file]", b"b")]), ("comma", [("files", b"a"), ("files", b"b")], [("items[file]", b"a"), ("items[file]", b"b")]), ("indices", [("files[0]", b"a"), ("files[1]", b"b")], [("items[0][file]", b"a"), ("items[1][file]", b"b")]), ], ) def test_array_format_controls_file_field_names( array_format: ArrayFormat, expected_top_level: list[tuple[str, FileTypes]], expected_nested: list[tuple[str, FileTypes]], ) -> None: top_level = {"files": [b"a", b"b"]} assert extract_files(top_level, paths=[["files", ""]], array_format=array_format) == expected_top_level nested = {"items": [{"file": b"a"}, {"file": b"b"}]} assert extract_files(nested, paths=[["items", "", "file"]], array_format=array_format) == expected_nested anthropic-sdk-python-0.120.2/tests/test_files.py000066400000000000000000000121571523216435200216270ustar00rootroot00000000000000from pathlib import Path import anyio import pytest from dirty_equals import IsDict, IsList, IsBytes, IsTuple from anthropic._files import to_httpx_files, deepcopy_with_paths, async_to_httpx_files from anthropic._utils import extract_files readme_path = Path(__file__).parent.parent.joinpath("README.md") def test_pathlib_includes_file_name() -> None: result = to_httpx_files({"file": readme_path}) print(result) assert result == IsDict({"file": IsTuple("README.md", IsBytes())}) def test_tuple_input() -> None: result = to_httpx_files([("file", readme_path)]) print(result) assert result == IsList(IsTuple("file", IsTuple("README.md", IsBytes()))) @pytest.mark.asyncio async def test_async_pathlib_includes_file_name() -> None: result = await async_to_httpx_files({"file": readme_path}) print(result) assert result == IsDict({"file": IsTuple("README.md", IsBytes())}) @pytest.mark.asyncio async def test_async_supports_anyio_path() -> None: result = await async_to_httpx_files({"file": anyio.Path(readme_path)}) print(result) assert result == IsDict({"file": IsTuple("README.md", IsBytes())}) @pytest.mark.asyncio async def test_async_tuple_input() -> None: result = await async_to_httpx_files([("file", readme_path)]) print(result) assert result == IsList(IsTuple("file", IsTuple("README.md", IsBytes()))) def test_string_not_allowed() -> None: with pytest.raises(TypeError, match="Expected file types input to be a FileContent type or to be a tuple"): to_httpx_files( { "file": "foo", # type: ignore } ) def assert_different_identities(obj1: object, obj2: object) -> None: assert obj1 == obj2 assert obj1 is not obj2 class TestDeepcopyWithPaths: def test_copies_top_level_dict(self) -> None: original = {"file": b"data", "other": "value"} result = deepcopy_with_paths(original, [["file"]]) assert_different_identities(result, original) def test_file_value_is_same_reference(self) -> None: file_bytes = b"contents" original = {"file": file_bytes} result = deepcopy_with_paths(original, [["file"]]) assert_different_identities(result, original) assert result["file"] is file_bytes def test_list_popped_wholesale(self) -> None: files = [b"f1", b"f2"] original = {"files": files, "title": "t"} result = deepcopy_with_paths(original, [["files", ""]]) assert_different_identities(result, original) result_files = result["files"] assert isinstance(result_files, list) assert_different_identities(result_files, files) def test_nested_array_path_copies_list_and_elements(self) -> None: elem1 = {"file": b"f1", "extra": 1} elem2 = {"file": b"f2", "extra": 2} original = {"items": [elem1, elem2]} result = deepcopy_with_paths(original, [["items", "", "file"]]) assert_different_identities(result, original) result_items = result["items"] assert isinstance(result_items, list) assert_different_identities(result_items, original["items"]) assert_different_identities(result_items[0], elem1) assert_different_identities(result_items[1], elem2) def test_empty_paths_returns_same_object(self) -> None: original = {"foo": "bar"} result = deepcopy_with_paths(original, []) assert result is original def test_multiple_paths(self) -> None: f1 = b"file1" f2 = b"file2" original = {"a": f1, "b": f2, "c": "unchanged"} result = deepcopy_with_paths(original, [["a"], ["b"]]) assert_different_identities(result, original) assert result["a"] is f1 assert result["b"] is f2 assert result["c"] is original["c"] def test_extract_files_does_not_mutate_original_top_level(self) -> None: file_bytes = b"contents" original = {"file": file_bytes, "other": "value"} copied = deepcopy_with_paths(original, [["file"]]) extracted = extract_files(copied, paths=[["file"]]) assert extracted == [("file", file_bytes)] assert original == {"file": file_bytes, "other": "value"} assert copied == {"other": "value"} def test_extract_files_does_not_mutate_original_nested_array_path(self) -> None: file1 = b"f1" file2 = b"f2" original = { "items": [ {"file": file1, "extra": 1}, {"file": file2, "extra": 2}, ], "title": "example", } copied = deepcopy_with_paths(original, [["items", "", "file"]]) extracted = extract_files(copied, paths=[["items", "", "file"]]) assert [entry for _, entry in extracted] == [file1, file2] assert original == { "items": [ {"file": file1, "extra": 1}, {"file": file2, "extra": 2}, ], "title": "example", } assert copied == { "items": [ {"extra": 1}, {"extra": 2}, ], "title": "example", } anthropic-sdk-python-0.120.2/tests/test_legacy_response.py000066400000000000000000000102201523216435200236740ustar00rootroot00000000000000import json from typing import Any, Union, cast from typing_extensions import Annotated import httpx import pytest import pydantic from anthropic import Anthropic, BaseModel from anthropic._streaming import Stream from anthropic._base_client import FinalRequestOptions from anthropic._legacy_response import LegacyAPIResponse class PydanticModel(pydantic.BaseModel): ... def test_response_parse_mismatched_basemodel(client: Anthropic) -> None: response = LegacyAPIResponse( raw=httpx.Response(200, content=b"foo"), client=client, stream=False, stream_cls=None, cast_to=str, options=FinalRequestOptions.construct(method="get", url="/foo"), ) with pytest.raises( TypeError, match="Pydantic models must subclass our base model type, e.g. `from anthropic import BaseModel`", ): response.parse(to=PydanticModel) @pytest.mark.parametrize( "content, expected", [ ("false", False), ("true", True), ("False", False), ("True", True), ("TrUe", True), ("FalSe", False), ], ) def test_response_parse_bool(client: Anthropic, content: str, expected: bool) -> None: response = LegacyAPIResponse( raw=httpx.Response(200, content=content), client=client, stream=False, stream_cls=None, cast_to=str, options=FinalRequestOptions.construct(method="get", url="/foo"), ) result = response.parse(to=bool) assert result is expected def test_response_parse_custom_stream(client: Anthropic) -> None: response = LegacyAPIResponse( raw=httpx.Response(200, content=b"foo"), client=client, stream=True, stream_cls=None, cast_to=str, options=FinalRequestOptions.construct(method="get", url="/foo"), ) stream = response.parse(to=Stream[int]) assert stream._cast_to == int class CustomModel(BaseModel): foo: str bar: int def test_response_parse_custom_model(client: Anthropic) -> None: response = LegacyAPIResponse( raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})), client=client, stream=False, stream_cls=None, cast_to=str, options=FinalRequestOptions.construct(method="get", url="/foo"), ) obj = response.parse(to=CustomModel) assert obj.foo == "hello!" assert obj.bar == 2 def test_response_basemodel_request_id(client: Anthropic) -> None: response = LegacyAPIResponse( raw=httpx.Response( 200, headers={"request-id": "my-req-id"}, content=json.dumps({"foo": "hello!", "bar": 2}), ), client=client, stream=False, stream_cls=None, cast_to=str, options=FinalRequestOptions.construct(method="get", url="/foo"), ) obj = response.parse(to=CustomModel) assert obj._request_id == "my-req-id" assert obj.foo == "hello!" assert obj.bar == 2 assert obj.to_dict() == {"foo": "hello!", "bar": 2} def test_response_parse_annotated_type(client: Anthropic) -> None: response = LegacyAPIResponse( raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})), client=client, stream=False, stream_cls=None, cast_to=str, options=FinalRequestOptions.construct(method="get", url="/foo"), ) obj = response.parse( to=cast("type[CustomModel]", Annotated[CustomModel, "random metadata"]), ) assert obj.foo == "hello!" assert obj.bar == 2 class OtherModel(pydantic.BaseModel): a: str @pytest.mark.parametrize("client", [False], indirect=True) # loose validation def test_response_parse_expect_model_union_non_json_content(client: Anthropic) -> None: response = LegacyAPIResponse( raw=httpx.Response(200, content=b"foo", headers={"Content-Type": "application/text"}), client=client, stream=False, stream_cls=None, cast_to=str, options=FinalRequestOptions.construct(method="get", url="/foo"), ) obj = response.parse(to=cast(Any, Union[CustomModel, OtherModel])) assert isinstance(obj, str) assert obj == "foo" anthropic-sdk-python-0.120.2/tests/test_middleware.py000066400000000000000000002442061523216435200226440ustar00rootroot00000000000000from __future__ import annotations import os import re import json from typing import Any, Protocol, cast from pathlib import Path from unittest import mock from typing_extensions import override import httpx import pytest from respx import MockRouter from anthropic import ( Stream, CallNext, Anthropic, APIRequest, Middleware, APIResponse, AsyncCallNext, AsyncAnthropic, RetryableError, AnthropicVertex, BadRequestError, AnthropicBedrock, AsyncAPIResponse, APIConnectionError, AsyncAnthropicVertex, AsyncAnthropicBedrock, AnthropicBedrockMantle, AsyncAnthropicBedrockMantle, ) from anthropic._models import FinalRequestOptions from anthropic.lib.aws import AnthropicAWS, AsyncAnthropicAWS from anthropic._response import BinaryAPIResponse, AsyncBinaryAPIResponse, StreamedBinaryAPIResponse from anthropic.lib.foundry import AnthropicFoundry, AsyncAnthropicFoundry from anthropic.types.message import Message from anthropic._legacy_response import LegacyAPIResponse base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") api_key = "my-anthropic-api-key" class MockRequestCall(Protocol): request: httpx.Request def _low_retry_timeout(*_args: Any, **_kwargs: Any) -> float: return 0.1 def make_sync_client(**kwargs: Any) -> Anthropic: return Anthropic(base_url=base_url, api_key=api_key, _strict_response_validation=True, **kwargs) def make_async_client(**kwargs: Any) -> AsyncAnthropic: return AsyncAnthropic(base_url=base_url, api_key=api_key, _strict_response_validation=True, **kwargs) def message_body(*, model: str = "claude-opus-4-6") -> dict[str, Any]: return { "id": "msg_013Zva2CMHLNnXjNJJKqJ2EF", "type": "message", "role": "assistant", "model": model, "content": [{"type": "text", "text": "Hello!"}], "stop_reason": "end_turn", "stop_sequence": None, "usage": {"input_tokens": 10, "output_tokens": 5}, } def error_body(*, type: str, message: str) -> dict[str, Any]: return {"type": "error", "error": {"type": type, "message": message}} def request_body(call: MockRequestCall) -> dict[str, Any]: return cast("dict[str, Any]", json.loads(call.request.content)) def middleware_request_body(request: APIRequest) -> dict[str, Any]: body = request.json assert isinstance(body, dict) return cast("dict[str, Any]", body) class RecordingMiddleware(Middleware): def __init__(self, name: str = "middleware", events: "list[str] | None" = None) -> None: self.name = name self.events = events if events is not None else [] self.requests: list[APIRequest] = [] self.results: list[Any] = [] @override def handle(self, request: APIRequest, call_next: CallNext) -> Any: self.requests.append(request) self.events.append(f"{self.name}:enter") result = call_next(request) self.events.append(f"{self.name}:exit") self.results.append(result) return result @override async def handle_async(self, request: APIRequest, call_next: AsyncCallNext) -> Any: self.requests.append(request) self.events.append(f"{self.name}:enter") result = await call_next(request) self.events.append(f"{self.name}:exit") self.results.append(result) return result class MutateBody(Middleware): def __init__(self, **changes: object) -> None: self.changes = changes def _mutate(self, request: APIRequest) -> APIRequest: body = request.json assert isinstance(body, dict) return request.copy(body={**cast("dict[str, object]", body), **self.changes}) @override def handle(self, request: APIRequest, call_next: CallNext) -> Any: return call_next(self._mutate(request)) @override async def handle_async(self, request: APIRequest, call_next: AsyncCallNext) -> Any: return await call_next(self._mutate(request)) class ModelFallback(Middleware): def __init__(self, fallback_model: str) -> None: self.fallback_model = fallback_model def _fallback(self, request: APIRequest) -> APIRequest: body = request.json assert isinstance(body, dict) return request.copy(body={**cast("dict[str, object]", body), "model": self.fallback_model}) @override def handle(self, request: APIRequest, call_next: CallNext) -> Any: response = call_next(request) if response.status_code == 529: return call_next(self._fallback(request)) return response @override async def handle_async(self, request: APIRequest, call_next: AsyncCallNext) -> Any: response = await call_next(request) if response.status_code == 529: return await call_next(self._fallback(request)) return response class ShrinkMaxTokensOnTooLarge(Middleware): def __init__(self, max_tokens: int) -> None: self.max_tokens = max_tokens def _shrink(self, request: APIRequest) -> APIRequest: body = request.json assert isinstance(body, dict) return request.copy(body={**cast("dict[str, object]", body), "max_tokens": self.max_tokens}) @override def handle(self, request: APIRequest, call_next: CallNext) -> Any: response = call_next(request) if response.status_code == 413: return call_next(self._shrink(request)) return response @override async def handle_async(self, request: APIRequest, call_next: AsyncCallNext) -> Any: response = await call_next(request) if response.status_code == 413: return await call_next(self._shrink(request)) return response class ShortCircuit(Middleware): def __init__(self, message: Message) -> None: self.message = message self.requests: list[APIRequest] = [] @override def handle(self, request: APIRequest, call_next: CallNext) -> Any: # noqa: ARG002 self.requests.append(request) return self.message @override async def handle_async(self, request: APIRequest, call_next: AsyncCallNext) -> Any: # noqa: ARG002 self.requests.append(request) return self.message class AttemptRecorder(Middleware): """Records the `retries_taken` and response status of every attempt the chain runs for.""" def __init__(self) -> None: self.attempts: list[int] = [] self.statuses: list[int] = [] self.errors: list[Exception] = [] @override def handle(self, request: APIRequest, call_next: CallNext) -> Any: self.attempts.append(request.retries_taken) try: response = call_next(request) except Exception as err: self.errors.append(err) raise self.statuses.append(response.status_code) return response @override async def handle_async(self, request: APIRequest, call_next: AsyncCallNext) -> Any: self.attempts.append(request.retries_taken) try: response = await call_next(request) except Exception as err: self.errors.append(err) raise self.statuses.append(response.status_code) return response class Boom(Exception): pass class Exploding(Middleware): @override def handle(self, request: APIRequest, call_next: CallNext) -> Any: # noqa: ARG002 raise Boom("middleware failure") @override async def handle_async(self, request: APIRequest, call_next: AsyncCallNext) -> Any: # noqa: ARG002 raise Boom("middleware failure") class SyncOnlyMiddleware(Middleware): @override def handle(self, request: APIRequest, call_next: CallNext) -> Any: return call_next(request) class AsyncOnlyMiddleware(Middleware): @override async def handle_async(self, request: APIRequest, call_next: AsyncCallNext) -> Any: return await call_next(request) class InspectResponse(Middleware): """Records the response wrapper returned by `call_next` and passes it through.""" def __init__(self) -> None: self.responses: list[Any] = [] @override def handle(self, request: APIRequest, call_next: CallNext) -> Any: response = call_next(request) self.responses.append(response) return response @override async def handle_async(self, request: APIRequest, call_next: AsyncCallNext) -> Any: response = await call_next(request) self.responses.append(response) return response class ParseInMiddleware(Middleware): """Parses the response inside the middleware before returning it.""" def __init__(self) -> None: self.parsed: list[Any] = [] @override def handle(self, request: APIRequest, call_next: CallNext) -> Any: response = call_next(request) self.parsed.append(response.parse()) return response @override async def handle_async(self, request: APIRequest, call_next: AsyncCallNext) -> Any: response = await call_next(request) self.parsed.append(await response.parse()) return response class AsyncCallableMiddleware: """A middleware object that is a class instance with an async `__call__` method.""" def __init__(self) -> None: self.requests: list[APIRequest] = [] async def __call__(self, request: APIRequest, call_next: AsyncCallNext) -> Any: self.requests.append(request) return await call_next(request) class SyncCallableMiddleware: """A middleware object that is a class instance with a sync `__call__` method.""" def __init__(self) -> None: self.requests: list[APIRequest] = [] def __call__(self, request: APIRequest, call_next: CallNext) -> Any: self.requests.append(request) return call_next(request) class AsyncHandleMiddleware(Middleware): """Invalid middleware: defines the sync `handle()` hook as an async function.""" @override async def handle(self, request: APIRequest, call_next: CallNext) -> Any: # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] return call_next(request) class SyncHandleAsyncMiddleware(Middleware): """Invalid middleware: defines the async `handle_async()` hook as a plain function.""" @override def handle_async(self, request: APIRequest, call_next: AsyncCallNext) -> Any: return call_next(request) class RetryOnInternalServerError(Middleware): """Fallback middleware that retries the same request once on a 5xx error. Records the request headers observed before each `call_next(...)` invocation so that tests can assert that both invocations saw identical request state. """ def __init__(self) -> None: self.headers_seen: list[dict[str, Any]] = [] self.results: list[Any] = [] @override def handle(self, request: APIRequest, call_next: CallNext) -> Any: self.headers_seen.append(dict(request.headers)) result = call_next(request) if result.status_code >= 500: self.headers_seen.append(dict(request.headers)) result = call_next(request) self.results.append(result) return result @override async def handle_async(self, request: APIRequest, call_next: AsyncCallNext) -> Any: self.headers_seen.append(dict(request.headers)) result = await call_next(request) if result.status_code >= 500: self.headers_seen.append(dict(request.headers)) result = await call_next(request) self.results.append(result) return result def short_circuit_message(*, model: str = "claude-opus-4-6") -> Message: return Message.construct(**message_body(model=model)) def file_metadata_body() -> dict[str, Any]: return { "id": "file_123", "created_at": "2024-01-01T00:00:00Z", "filename": "upload.txt", "mime_type": "text/plain", "size_bytes": 13, "type": "file", } class TestAPIRequest: def test_properties(self) -> None: options = FinalRequestOptions.construct( method="post", url="/v1/messages", json_data={"model": "claude-opus-4-6", "max_tokens": 16}, headers={"x-foo": "bar"}, params={"beta": "true"}, max_retries=3, timeout=10.0, ) request = APIRequest(options=options, cast_to=Message, stream=False, stream_cls=None) assert request.method == "post" assert request.url == "/v1/messages" assert request.json == {"model": "claude-opus-4-6", "max_tokens": 16} assert request.headers == {"x-foo": "bar"} assert request.query_params == {"beta": "true"} assert request.timeout == 10.0 assert request.max_retries == 3 assert request.cast_to is Message assert request.stream is False assert request.stream_cls is None assert request.options is options def test_headers_default_to_empty_mapping(self) -> None: options = FinalRequestOptions(method="get", url="/v1/models") request = APIRequest(options=options, cast_to=Message) assert request.headers == {} def test_copy_returns_new_instance_with_changes(self) -> None: options = FinalRequestOptions.construct( method="post", url="/v1/messages", json_data={"model": "claude-opus-4-6", "max_tokens": 16}, max_retries=3, ) request = APIRequest(options=options, cast_to=Message) copied = request.copy(body={"model": "claude-sonnet-4-5"}, headers={"x-trace-id": "123"}) assert copied is not request assert copied.options is not request.options assert copied.json == {"model": "claude-sonnet-4-5"} assert copied.headers == {"x-trace-id": "123"} assert copied.method == "post" assert copied.url == "/v1/messages" assert copied.cast_to is Message assert copied.stream is False # the original request is untouched assert request.json == {"model": "claude-opus-4-6", "max_tokens": 16} assert request.headers == {} def test_retries_taken_defaults_to_zero_and_copy_preserves_it(self) -> None: options = FinalRequestOptions.construct(method="post", url="/v1/messages") request = APIRequest(options=options, cast_to=Message) assert request.retries_taken == 0 attempt = APIRequest(options=options, cast_to=Message, retries_taken=2) assert attempt.copy(headers={"x-trace-id": "123"}).retries_taken == 2 def test_copy_deep_copies_body(self) -> None: body: dict[str, Any] = {"model": "claude-opus-4-6", "messages": [{"role": "user", "content": "hi"}]} options = FinalRequestOptions.construct(method="post", url="/v1/messages", json_data=body) request = APIRequest(options=options, cast_to=Message) copied = request.copy() copied_body = copied.json assert isinstance(copied_body, dict) cast("dict[str, Any]", copied_body)["messages"][0]["content"] = "changed" assert request.json == {"model": "claude-opus-4-6", "messages": [{"role": "user", "content": "hi"}]} def test_copy_does_not_deep_copy_file_objects(self, tmp_path: Path) -> None: # file/IO objects cannot be deep-copied; `copy()` must carry them over by reference path = tmp_path / "upload.txt" path.write_bytes(b"file contents") with path.open("rb") as reader: options = FinalRequestOptions.construct( method="post", url="/v1/files?beta=true", json_data={"purpose": "test"}, files=[("file", reader)], ) request = APIRequest(options=options, cast_to=Message) copied = request.copy(headers={"x-trace-id": "123"}) files = cast("list[tuple[str, Any]]", copied.options.files) assert files is not None assert files[0][1] is reader # mutating the copy must never affect the original request copied_body = copied.json assert isinstance(copied_body, dict) cast("dict[str, Any]", copied_body)["purpose"] = "changed" assert request.json == {"purpose": "test"} assert request.headers == {} def test_copy_header_mutations_do_not_affect_original(self) -> None: options = FinalRequestOptions.construct( method="post", url="/v1/messages", headers={"x-foo": "bar"}, json_data={"model": "claude-opus-4-6"}, ) request = APIRequest(options=options, cast_to=Message) copied = request.copy() copied_headers = copied.options.headers assert isinstance(copied_headers, dict) cast("dict[str, str]", copied_headers)["x-added"] = "value" cast("dict[str, str]", copied_headers)["x-foo"] = "changed" assert request.headers == {"x-foo": "bar"} class TestDefaultMiddleware: def test_handle_is_passthrough(self) -> None: sentinel = object() request = APIRequest(options=FinalRequestOptions(method="post", url="/v1/messages"), cast_to=Message) seen: list[APIRequest] = [] def handler(req: APIRequest) -> Any: seen.append(req) return sentinel assert Middleware().handle(request, handler) is sentinel assert seen == [request] async def test_handle_async_is_passthrough(self) -> None: sentinel = object() request = APIRequest(options=FinalRequestOptions(method="post", url="/v1/messages"), cast_to=Message) seen: list[APIRequest] = [] async def handler(req: APIRequest) -> Any: seen.append(req) return sentinel assert await Middleware().handle_async(request, handler) is sentinel assert seen == [request] class TestMiddlewareValidation: def test_sync_client_rejects_async_callable_object(self) -> None: with pytest.raises(TypeError, match="is an async function"): make_sync_client(middleware=[AsyncCallableMiddleware()]) def test_async_client_accepts_async_callable_object(self) -> None: middleware = AsyncCallableMiddleware() client = make_async_client(middleware=[middleware]) assert client._middleware == (middleware,) @pytest.mark.respx(base_url=base_url) async def test_async_callable_object_handles_requests(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) middleware = AsyncCallableMiddleware() client = make_async_client(middleware=[middleware]) message = await client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(message, Message) assert len(middleware.requests) == 1 def test_async_client_rejects_sync_callable_object(self) -> None: with pytest.raises(TypeError, match="is not an async function"): make_async_client(middleware=[SyncCallableMiddleware()]) def test_sync_client_accepts_sync_callable_object(self) -> None: middleware = SyncCallableMiddleware() client = make_sync_client(middleware=[middleware]) assert client._middleware == (middleware,) @pytest.mark.respx(base_url=base_url) def test_sync_callable_object_handles_requests(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) middleware = SyncCallableMiddleware() client = make_sync_client(middleware=[middleware]) message = client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(message, Message) assert len(middleware.requests) == 1 def test_sync_client_rejects_non_callable(self) -> None: with pytest.raises(TypeError, match="is not callable"): make_sync_client(middleware=[cast(Any, object())]) def test_async_client_rejects_non_callable(self) -> None: with pytest.raises(TypeError, match="is not callable"): make_async_client(middleware=[cast(Any, object())]) def test_sync_client_rejects_async_handle_override(self) -> None: with pytest.raises(TypeError, match=r"defines `handle\(\)` as an async function"): make_sync_client(middleware=[AsyncHandleMiddleware()]) def test_async_client_rejects_sync_handle_async_override(self) -> None: with pytest.raises(TypeError, match=r"defines `handle_async\(\)` as a sync function"): make_async_client(middleware=[SyncHandleAsyncMiddleware()]) class TestSyncMiddleware: @pytest.mark.respx(base_url=base_url) def test_middleware_sees_request_and_returns_response(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) recorder = RecordingMiddleware() client = make_sync_client(middleware=[recorder]) message = client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(message, Message) assert message.id == "msg_013Zva2CMHLNnXjNJJKqJ2EF" assert len(recorder.requests) == 1 request = recorder.requests[0] assert request.method == "post" assert request.url == "/v1/messages" assert request.cast_to is Message assert request.stream is False body = middleware_request_body(request) assert body["model"] == "claude-opus-4-6" assert body["max_tokens"] == 1024 # the middleware itself saw the `APIResponse` wrapper; the value the caller # receives is parsed from that same response assert len(recorder.results) == 1 response = recorder.results[0] assert isinstance(response, APIResponse) assert response.parse() is message @pytest.mark.respx(base_url=base_url) def test_middleware_ordering(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) events: list[str] = [] outer = RecordingMiddleware("outer", events) inner = RecordingMiddleware("inner", events) client = make_sync_client(middleware=[outer, inner]) client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert events == ["outer:enter", "inner:enter", "inner:exit", "outer:exit"] @pytest.mark.respx(base_url=base_url) def test_request_mutation_changes_outgoing_request(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) client = make_sync_client(middleware=[MutateBody(model="claude-sonnet-4-5", max_tokens=4096)]) message = client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(message, Message) calls = cast("list[MockRequestCall]", respx_mock.calls) assert len(calls) == 1 sent = request_body(calls[0]) assert sent["model"] == "claude-sonnet-4-5" assert sent["max_tokens"] == 4096 assert sent["messages"] == [{"role": "user", "content": "Hello"}] @pytest.mark.respx(base_url=base_url) def test_fallback_on_overloaded_error(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( side_effect=[ httpx.Response(529, json=error_body(type="overloaded_error", message="Overloaded")), httpx.Response(200, json=message_body(model="claude-sonnet-4-5")), ] ) client = make_sync_client(middleware=[ModelFallback("claude-sonnet-4-5")], max_retries=0) message = client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(message, Message) assert message.model == "claude-sonnet-4-5" calls = cast("list[MockRequestCall]", respx_mock.calls) assert len(calls) == 2 assert request_body(calls[0])["model"] == "claude-opus-4-6" assert request_body(calls[1])["model"] == "claude-sonnet-4-5" @pytest.mark.respx(base_url=base_url) def test_retry_with_modified_params_on_request_too_large(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( side_effect=[ httpx.Response(413, json=error_body(type="invalid_request_error", message="Request too large")), httpx.Response(200, json=message_body()), ] ) client = make_sync_client(middleware=[ShrinkMaxTokensOnTooLarge(256)]) message = client.messages.create( max_tokens=4096, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(message, Message) calls = cast("list[MockRequestCall]", respx_mock.calls) assert len(calls) == 2 assert request_body(calls[0])["max_tokens"] == 4096 assert request_body(calls[1])["max_tokens"] == 256 @pytest.mark.respx(base_url=base_url, assert_all_called=False) def test_short_circuit_skips_http_request(self, respx_mock: MockRouter) -> None: route = respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) cached = short_circuit_message(model="claude-cached") middleware = ShortCircuit(cached) client = make_sync_client(middleware=[middleware]) message = client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert message is cached assert len(middleware.requests) == 1 assert route.call_count == 0 @pytest.mark.respx(base_url=base_url) def test_functional_middleware(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) def add_trace_header(request: APIRequest, call_next: CallNext) -> Any: return call_next(request.copy(headers={**request.headers, "x-trace-id": "abc-123"})) client = make_sync_client(middleware=[add_trace_header]) message = client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(message, Message) calls = cast("list[MockRequestCall]", respx_mock.calls) assert calls[0].request.headers["x-trace-id"] == "abc-123" @mock.patch("anthropic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) def test_middleware_runs_per_attempt(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( side_effect=[ httpx.Response(500), httpx.Response(500), httpx.Response(200, json=message_body()), ] ) recorder = AttemptRecorder() client = make_sync_client(middleware=[recorder], max_retries=2) message = client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(message, Message) # the chain ran once per HTTP attempt, seeing each failure's response; # returning an error response keeps it on the SDK's retry path assert recorder.attempts == [0, 1, 2] assert recorder.statuses == [500, 500, 200] assert recorder.errors == [] assert len(respx_mock.calls) == 3 @mock.patch("anthropic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url, assert_all_called=False) def test_middleware_error_is_not_retried(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) recorder = AttemptRecorder() client = make_sync_client(middleware=[recorder, Exploding()], max_retries=2) with pytest.raises(Boom): client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) # the middleware's own error propagates immediately, without re-running the chain assert recorder.attempts == [0] assert len(respx_mock.calls) == 0 @mock.patch("anthropic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url, assert_all_called=False) def test_retryable_error_is_retried_then_propagates(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) attempts: list[int] = [] def give_up(request: APIRequest, call_next: CallNext) -> Any: # noqa: ARG001 attempts.append(request.retries_taken) raise RetryableError("try again") client = make_sync_client(middleware=[give_up], max_retries=2) with pytest.raises(RetryableError): client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert attempts == [0, 1, 2] assert len(respx_mock.calls) == 0 @mock.patch("anthropic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) def test_error_with_retryable_cause_is_retried(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( side_effect=[ httpx.ConnectError("kaboom"), httpx.Response(200, json=message_body()), ] ) def wrap_errors(request: APIRequest, call_next: CallNext) -> Any: try: return call_next(request) except APIConnectionError as err: # wrapping a retryable failure with `raise ... from` keeps it on the retry path raise Boom("wrapped") from err client = make_sync_client(middleware=[wrap_errors], max_retries=2) message = client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(message, Message) assert len(respx_mock.calls) == 2 @mock.patch("anthropic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) def test_returned_error_response_raises_typed_error_for_caller(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(400, json=error_body(type="invalid_request_error", message="bad request")) ) recorder = AttemptRecorder() client = make_sync_client(middleware=[recorder], max_retries=2) with pytest.raises(BadRequestError): client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) # middleware saw the error response rather than an exception; the SDK # raised the typed error for the caller, without retrying the 400 assert recorder.statuses == [400] assert recorder.errors == [] assert len(respx_mock.calls) == 1 @mock.patch("anthropic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) def test_request_modifications_do_not_persist_across_attempts(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( side_effect=[ httpx.Response(500), httpx.Response(200, json=message_body()), ] ) def tag_attempt(request: APIRequest, call_next: CallNext) -> Any: # each attempt starts from the original request, not the previous attempt's copy assert "x-attempt" not in request.headers return call_next(request.copy(headers={**request.headers, "x-attempt": str(request.retries_taken)})) client = make_sync_client(middleware=[tag_attempt], max_retries=2) message = client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(message, Message) calls = cast("list[MockRequestCall]", respx_mock.calls) assert calls[0].request.headers["x-attempt"] == "0" assert calls[1].request.headers["x-attempt"] == "1" @pytest.mark.respx(base_url=base_url) def test_streaming_flows_through_middleware(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, headers={"content-type": "text/event-stream"}, content=b"") ) recorder = RecordingMiddleware() client = make_sync_client(middleware=[recorder]) stream = client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", stream=True, ) assert isinstance(stream, Stream) assert len(recorder.requests) == 1 assert recorder.requests[0].stream is True assert recorder.requests[0].stream_cls is not None # the middleware sees the `APIResponse` wrapper, not the `Stream` assert isinstance(recorder.results[0], APIResponse) stream.close() @pytest.mark.respx(base_url=base_url) def test_streaming_error_response_is_visible_to_middleware(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( side_effect=[ httpx.Response(529, json=error_body(type="overloaded_error", message="Overloaded")), httpx.Response(200, headers={"content-type": "text/event-stream"}, content=b""), ] ) client = make_sync_client(middleware=[ModelFallback("claude-sonnet-4-5")], max_retries=0) stream = client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", stream=True, ) assert isinstance(stream, Stream) calls = cast("list[MockRequestCall]", respx_mock.calls) assert len(calls) == 2 assert request_body(calls[1])["model"] == "claude-sonnet-4-5" stream.close() @pytest.mark.respx(base_url=base_url) def test_call_next_returns_api_response(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, json=message_body(), headers={"x-custom-header": "custom-value"}) ) middleware = InspectResponse() client = make_sync_client(middleware=[middleware]) message = client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(message, Message) assert len(middleware.responses) == 1 response = middleware.responses[0] assert isinstance(response, APIResponse) assert response.status_code == 200 assert response.headers["x-custom-header"] == "custom-value" @pytest.mark.respx(base_url=base_url) def test_parse_in_middleware_shares_parse_cache(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) middleware = ParseInMiddleware() client = make_sync_client(middleware=[middleware]) message = client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) # the value parsed inside the middleware is the same object the caller receives assert isinstance(message, Message) assert middleware.parsed == [message] assert middleware.parsed[0] is message @pytest.mark.respx(base_url=base_url) def test_with_raw_response_flows_through_middleware(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) recorder = RecordingMiddleware() client = make_sync_client(middleware=[recorder]) response = client.messages.with_raw_response.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(response, LegacyAPIResponse) assert isinstance(response.parse(), Message) assert len(recorder.requests) == 1 assert recorder.requests[0].url == "/v1/messages" @pytest.mark.respx(base_url=base_url) def test_with_raw_response_middleware_sees_api_response(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, json=message_body(), headers={"x-custom-header": "custom-value"}) ) middleware = InspectResponse() client = make_sync_client(middleware=[middleware]) response = client.messages.with_raw_response.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) # the middleware itself saw a true `APIResponse` assert len(middleware.responses) == 1 assert isinstance(middleware.responses[0], APIResponse) # the caller still receives the `LegacyAPIResponse` wrapper it expects assert isinstance(response, LegacyAPIResponse) assert response.status_code == 200 assert response.headers["x-custom-header"] == "custom-value" message = response.parse() assert isinstance(message, Message) assert message.id == "msg_013Zva2CMHLNnXjNJJKqJ2EF" @pytest.mark.respx(base_url=base_url) def test_with_streaming_response_middleware_sees_api_response(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, json=message_body(), headers={"x-custom-header": "custom-value"}) ) middleware = InspectResponse() client = make_sync_client(middleware=[middleware]) with client.messages.with_streaming_response.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) as response: # the middleware itself saw a true `APIResponse` assert len(middleware.responses) == 1 assert isinstance(middleware.responses[0], APIResponse) # the caller still receives the `APIResponse` wrapper it expects assert isinstance(response, APIResponse) assert response.status_code == 200 assert response.headers["x-custom-header"] == "custom-value" message = response.parse() assert isinstance(message, Message) assert message.id == "msg_013Zva2CMHLNnXjNJJKqJ2EF" @pytest.mark.respx(base_url=base_url) def test_response_wrapper_cast_to_is_returned_unparsed(self, respx_mock: MockRouter) -> None: respx_mock.get("/v1/files/file_id/content?beta=true").mock( return_value=httpx.Response(200, content=b"file contents") ) recorder = RecordingMiddleware() client = make_sync_client(middleware=[recorder]) # `download()` asks for the `BinaryAPIResponse` wrapper itself via `cast_to`; # the middleware chain must hand it back instead of parsing it file = client.beta.files.download(file_id="file_id") assert isinstance(file, BinaryAPIResponse) assert file.read() == b"file contents" assert len(recorder.requests) == 1 # the middleware also saw the typed wrapper, not a generic `APIResponse` assert len(recorder.results) == 1 assert isinstance(recorder.results[0], BinaryAPIResponse) assert recorder.results[0] is file @pytest.mark.respx(base_url=base_url) def test_fallback_retry_sees_identical_request_state(self, respx_mock: MockRouter) -> None: # use `with_streaming_response.download` as it relies on the internal cast_to # override header which the request pipeline consumes; a retrying middleware # must observe identical request state on every `call_next(...)` invocation respx_mock.get("/v1/files/file_id/content?beta=true").mock( side_effect=[ httpx.Response(503, json=error_body(type="api_error", message="Service unavailable")), httpx.Response(200, json={"foo": "bar"}), ] ) middleware = RetryOnInternalServerError() client = make_sync_client(middleware=[middleware], max_retries=0) with client.beta.files.with_streaming_response.download(file_id="file_id") as file: assert isinstance(file, StreamedBinaryAPIResponse) assert json.loads(file.read()) == {"foo": "bar"} calls = cast("list[MockRequestCall]", respx_mock.calls) assert len(calls) == 2 # both invocations observed identical request state, including the # internal cast_to override header assert len(middleware.headers_seen) == 2 assert middleware.headers_seen[0] == middleware.headers_seen[1] # and the successful retry produced the correctly-typed response assert len(middleware.results) == 1 assert isinstance(middleware.results[0], StreamedBinaryAPIResponse) assert not isinstance(middleware.results[0], BinaryAPIResponse) @pytest.mark.respx(base_url=base_url) def test_middleware_ordering_across_sequential_requests(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) events: list[str] = [] outer = RecordingMiddleware("outer", events) inner = RecordingMiddleware("inner", events) client = make_sync_client(middleware=[outer, inner]) # the chain is built once at construction time and reused for every request chain = client._middleware_chain assert chain is not None for _ in range(3): client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert client._middleware_chain is chain assert events == ["outer:enter", "inner:enter", "inner:exit", "outer:exit"] * 3 @pytest.mark.respx(base_url=base_url) def test_middleware_iterator_argument_runs_middleware(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) recorder = RecordingMiddleware() client = make_sync_client(middleware=iter([recorder])) assert client._middleware == (recorder,) message = client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(message, Message) assert len(recorder.requests) == 1 @pytest.mark.respx(base_url=base_url) def test_file_upload_with_request_copying_middleware(self, respx_mock: MockRouter, tmp_path: Path) -> None: respx_mock.post("/v1/files?beta=true").mock(return_value=httpx.Response(200, json=file_metadata_body())) def add_trace_header(request: APIRequest, call_next: CallNext) -> Any: return call_next(request.copy(headers={**request.headers, "x-trace-id": "abc-123"})) client = make_sync_client(middleware=[add_trace_header]) # uploading an open file handle must not crash when middleware copies the request path = tmp_path / "upload.txt" path.write_bytes(b"file contents") with path.open("rb") as f: file = client.beta.files.upload(file=f) assert file.id == "file_123" calls = cast("list[MockRequestCall]", respx_mock.calls) assert len(calls) == 1 assert calls[0].request.headers["x-trace-id"] == "abc-123" def test_copy_inherits_replaces_and_clears_middleware(self) -> None: first = RecordingMiddleware("first") second = RecordingMiddleware("second") client = make_sync_client(middleware=[first]) assert client._middleware == (first,) assert client.copy()._middleware == (first,) assert client.with_options(max_retries=7)._middleware == (first,) assert client.copy(middleware=[second])._middleware == (second,) assert client.copy(middleware=[])._middleware == () assert client.copy(middleware=None)._middleware == () def test_with_middleware_appends_without_mutating_original(self) -> None: first = RecordingMiddleware("first") second = RecordingMiddleware("second") third = RecordingMiddleware("third") client = make_sync_client(middleware=[first]) derived = client.with_middleware(second, third) assert derived is not client assert derived._middleware == (first, second, third) assert client._middleware == (first,) def test_with_middleware_validates_like_the_constructor(self) -> None: client = make_sync_client() async def async_only(request: APIRequest, call_next: AsyncCallNext) -> Any: return await call_next(request) with pytest.raises(TypeError): client.with_middleware(async_only) @pytest.mark.respx(base_url=base_url) def test_with_middleware_client_runs_appended_middleware_innermost(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) events: list[str] = [] outer = RecordingMiddleware("outer", events=events) extra = RecordingMiddleware("extra", events=events) client = make_sync_client(middleware=[outer]) client.with_middleware(extra).messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) # the appended middleware runs inside the client's own middleware assert events == ["outer:enter", "extra:enter", "extra:exit", "outer:exit"] # the original client is unaffected by the derived client's calls events.clear() client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert events == ["outer:enter", "outer:exit"] @pytest.mark.respx(base_url=base_url) def test_with_options_client_runs_inherited_middleware(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) recorder = RecordingMiddleware() client = make_sync_client(middleware=[recorder]) client.with_options(max_retries=0).messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert len(recorder.requests) == 1 @pytest.mark.respx(base_url=base_url, assert_all_called=False) def test_middleware_exception_propagates(self, respx_mock: MockRouter) -> None: route = respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) client = make_sync_client(middleware=[Exploding()]) with pytest.raises(Boom, match="middleware failure"): client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert route.call_count == 0 @pytest.mark.respx(base_url=base_url) def test_no_middleware_behavior_unchanged(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) client = make_sync_client() assert client._middleware == () message = client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(message, Message) def test_construction_rejects_async_only_middleware(self) -> None: with pytest.raises(TypeError, match="does not implement `handle\\(\\)`"): make_sync_client(middleware=[AsyncOnlyMiddleware()]) def test_construction_rejects_async_function(self) -> None: async def async_fn(request: APIRequest, call_next: AsyncCallNext) -> Any: return await call_next(request) with pytest.raises(TypeError, match="is an async function"): make_sync_client(middleware=[async_fn]) def test_construction_accepts_sync_middleware(self) -> None: def sync_fn(request: APIRequest, call_next: CallNext) -> Any: return call_next(request) client = make_sync_client(middleware=[SyncOnlyMiddleware(), RecordingMiddleware(), sync_fn]) assert len(client._middleware) == 3 class TestAsyncMiddleware: @pytest.mark.respx(base_url=base_url) async def test_middleware_sees_request_and_returns_response(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) recorder = RecordingMiddleware() client = make_async_client(middleware=[recorder]) message = await client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(message, Message) assert len(recorder.requests) == 1 request = recorder.requests[0] assert request.method == "post" assert request.url == "/v1/messages" assert request.cast_to is Message assert request.stream is False assert middleware_request_body(request)["model"] == "claude-opus-4-6" # the middleware itself saw the `AsyncAPIResponse` wrapper; the value the # caller receives is parsed from that same response assert len(recorder.results) == 1 response = recorder.results[0] assert isinstance(response, AsyncAPIResponse) assert await response.parse() is message @pytest.mark.respx(base_url=base_url) async def test_middleware_ordering(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) events: list[str] = [] outer = RecordingMiddleware("outer", events) inner = RecordingMiddleware("inner", events) client = make_async_client(middleware=[outer, inner]) await client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert events == ["outer:enter", "inner:enter", "inner:exit", "outer:exit"] @pytest.mark.respx(base_url=base_url) async def test_request_mutation_changes_outgoing_request(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) client = make_async_client(middleware=[MutateBody(model="claude-sonnet-4-5", max_tokens=4096)]) message = await client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(message, Message) calls = cast("list[MockRequestCall]", respx_mock.calls) assert len(calls) == 1 sent = request_body(calls[0]) assert sent["model"] == "claude-sonnet-4-5" assert sent["max_tokens"] == 4096 @pytest.mark.respx(base_url=base_url) async def test_fallback_on_overloaded_error(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( side_effect=[ httpx.Response(529, json=error_body(type="overloaded_error", message="Overloaded")), httpx.Response(200, json=message_body(model="claude-sonnet-4-5")), ] ) client = make_async_client(middleware=[ModelFallback("claude-sonnet-4-5")], max_retries=0) message = await client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(message, Message) assert message.model == "claude-sonnet-4-5" calls = cast("list[MockRequestCall]", respx_mock.calls) assert len(calls) == 2 assert request_body(calls[0])["model"] == "claude-opus-4-6" assert request_body(calls[1])["model"] == "claude-sonnet-4-5" @pytest.mark.respx(base_url=base_url, assert_all_called=False) async def test_short_circuit_skips_http_request(self, respx_mock: MockRouter) -> None: route = respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) cached = short_circuit_message(model="claude-cached") middleware = ShortCircuit(cached) client = make_async_client(middleware=[middleware]) message = await client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert message is cached assert len(middleware.requests) == 1 assert route.call_count == 0 @pytest.mark.respx(base_url=base_url) async def test_functional_middleware(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) async def add_trace_header(request: APIRequest, call_next: AsyncCallNext) -> Any: return await call_next(request.copy(headers={**request.headers, "x-trace-id": "abc-123"})) client = make_async_client(middleware=[add_trace_header]) message = await client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(message, Message) calls = cast("list[MockRequestCall]", respx_mock.calls) assert calls[0].request.headers["x-trace-id"] == "abc-123" @pytest.mark.respx(base_url=base_url) async def test_call_next_returns_api_response(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, json=message_body(), headers={"x-custom-header": "custom-value"}) ) middleware = InspectResponse() client = make_async_client(middleware=[middleware]) message = await client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(message, Message) assert len(middleware.responses) == 1 response = middleware.responses[0] assert isinstance(response, AsyncAPIResponse) assert response.status_code == 200 assert response.headers["x-custom-header"] == "custom-value" @pytest.mark.respx(base_url=base_url) async def test_parse_in_middleware_shares_parse_cache(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) middleware = ParseInMiddleware() client = make_async_client(middleware=[middleware]) message = await client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) # the value parsed inside the middleware is the same object the caller receives assert isinstance(message, Message) assert middleware.parsed == [message] assert middleware.parsed[0] is message @pytest.mark.respx(base_url=base_url) async def test_with_raw_response_flows_through_middleware(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) recorder = RecordingMiddleware() client = make_async_client(middleware=[recorder]) response = await client.messages.with_raw_response.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(response, LegacyAPIResponse) assert isinstance(response.parse(), Message) assert len(recorder.requests) == 1 assert recorder.requests[0].url == "/v1/messages" @pytest.mark.respx(base_url=base_url) async def test_with_raw_response_middleware_sees_api_response(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, json=message_body(), headers={"x-custom-header": "custom-value"}) ) middleware = InspectResponse() client = make_async_client(middleware=[middleware]) response = await client.messages.with_raw_response.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) # the middleware itself saw a true `AsyncAPIResponse` assert len(middleware.responses) == 1 assert isinstance(middleware.responses[0], AsyncAPIResponse) # the caller still receives the `LegacyAPIResponse` wrapper it expects assert isinstance(response, LegacyAPIResponse) assert response.status_code == 200 assert response.headers["x-custom-header"] == "custom-value" message = response.parse() assert isinstance(message, Message) assert message.id == "msg_013Zva2CMHLNnXjNJJKqJ2EF" @pytest.mark.respx(base_url=base_url) async def test_with_streaming_response_middleware_sees_api_response(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( return_value=httpx.Response(200, json=message_body(), headers={"x-custom-header": "custom-value"}) ) middleware = InspectResponse() client = make_async_client(middleware=[middleware]) async with client.messages.with_streaming_response.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) as response: # the middleware itself saw a true `AsyncAPIResponse` assert len(middleware.responses) == 1 assert isinstance(middleware.responses[0], AsyncAPIResponse) # the caller still receives the `AsyncAPIResponse` wrapper it expects assert isinstance(response, AsyncAPIResponse) assert response.status_code == 200 assert response.headers["x-custom-header"] == "custom-value" message = await response.parse() assert isinstance(message, Message) assert message.id == "msg_013Zva2CMHLNnXjNJJKqJ2EF" @pytest.mark.respx(base_url=base_url) async def test_response_wrapper_cast_to_is_returned_unparsed(self, respx_mock: MockRouter) -> None: respx_mock.get("/v1/files/file_id/content?beta=true").mock( return_value=httpx.Response(200, content=b"file contents") ) recorder = RecordingMiddleware() client = make_async_client(middleware=[recorder]) # `download()` asks for the `AsyncBinaryAPIResponse` wrapper itself via `cast_to`; # the middleware chain must hand it back instead of parsing it file = await client.beta.files.download(file_id="file_id") assert isinstance(file, AsyncBinaryAPIResponse) assert await file.read() == b"file contents" assert len(recorder.requests) == 1 # the middleware also saw the typed wrapper, not a generic `AsyncAPIResponse` assert len(recorder.results) == 1 assert isinstance(recorder.results[0], AsyncBinaryAPIResponse) assert recorder.results[0] is file @pytest.mark.respx(base_url=base_url) async def test_fallback_retry_sees_identical_request_state(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( side_effect=[ httpx.Response(503, json=error_body(type="api_error", message="Service unavailable")), httpx.Response(200, json=message_body()), ] ) middleware = RetryOnInternalServerError() client = make_async_client(middleware=[middleware], max_retries=0) message = await client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(message, Message) calls = cast("list[MockRequestCall]", respx_mock.calls) assert len(calls) == 2 # both invocations observed identical request state assert len(middleware.headers_seen) == 2 assert middleware.headers_seen[0] == middleware.headers_seen[1] # and both outgoing requests carried an identical body assert request_body(calls[0]) == request_body(calls[1]) @pytest.mark.respx(base_url=base_url) async def test_middleware_ordering_across_sequential_requests(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) events: list[str] = [] outer = RecordingMiddleware("outer", events) inner = RecordingMiddleware("inner", events) client = make_async_client(middleware=[outer, inner]) # the chain is built once at construction time and reused for every request chain = client._middleware_chain assert chain is not None for _ in range(3): await client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert client._middleware_chain is chain assert events == ["outer:enter", "inner:enter", "inner:exit", "outer:exit"] * 3 @pytest.mark.respx(base_url=base_url) async def test_middleware_iterator_argument_runs_middleware(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) recorder = RecordingMiddleware() client = make_async_client(middleware=iter([recorder])) assert client._middleware == (recorder,) message = await client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(message, Message) assert len(recorder.requests) == 1 @pytest.mark.respx(base_url=base_url, assert_all_called=False) async def test_middleware_exception_propagates(self, respx_mock: MockRouter) -> None: route = respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) client = make_async_client(middleware=[Exploding()]) with pytest.raises(Boom, match="middleware failure"): await client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert route.call_count == 0 @mock.patch("anthropic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url) async def test_middleware_runs_per_attempt(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock( side_effect=[ httpx.Response(500), httpx.Response(500), httpx.Response(200, json=message_body()), ] ) recorder = AttemptRecorder() client = make_async_client(middleware=[recorder], max_retries=2) message = await client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(message, Message) # the chain ran once per HTTP attempt, seeing each failure's response; # returning an error response keeps it on the SDK's retry path assert recorder.attempts == [0, 1, 2] assert recorder.statuses == [500, 500, 200] assert recorder.errors == [] assert len(respx_mock.calls) == 3 @mock.patch("anthropic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url, assert_all_called=False) async def test_retryable_error_is_retried_then_propagates(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) attempts: list[int] = [] async def give_up(request: APIRequest, call_next: AsyncCallNext) -> Any: # noqa: ARG001 attempts.append(request.retries_taken) raise RetryableError("try again") client = make_async_client(middleware=[give_up], max_retries=2) with pytest.raises(RetryableError): await client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert attempts == [0, 1, 2] assert len(respx_mock.calls) == 0 @mock.patch("anthropic._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout) @pytest.mark.respx(base_url=base_url, assert_all_called=False) async def test_middleware_error_is_not_retried(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) recorder = AttemptRecorder() client = make_async_client(middleware=[recorder, Exploding()], max_retries=2) with pytest.raises(Boom): await client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) # the middleware's own error propagates immediately, without re-running the chain assert recorder.attempts == [0] assert len(respx_mock.calls) == 0 def test_copy_inherits_replaces_and_clears_middleware(self) -> None: first = RecordingMiddleware("first") second = RecordingMiddleware("second") client = make_async_client(middleware=[first]) assert client._middleware == (first,) assert client.copy()._middleware == (first,) assert client.with_options(max_retries=7)._middleware == (first,) assert client.copy(middleware=[second])._middleware == (second,) assert client.copy(middleware=[])._middleware == () assert client.copy(middleware=None)._middleware == () @pytest.mark.respx(base_url=base_url) async def test_with_middleware_client_runs_appended_middleware_innermost(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) events: list[str] = [] outer = RecordingMiddleware("outer", events=events) extra = RecordingMiddleware("extra", events=events) client = make_async_client(middleware=[outer]) derived = client.with_middleware(extra) assert derived._middleware == (outer, extra) assert client._middleware == (outer,) await derived.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) # the appended middleware runs inside the client's own middleware assert events == ["outer:enter", "extra:enter", "extra:exit", "outer:exit"] @pytest.mark.respx(base_url=base_url) async def test_no_middleware_behavior_unchanged(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) client = make_async_client() assert client._middleware == () message = await client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(message, Message) def test_construction_rejects_sync_only_middleware(self) -> None: with pytest.raises(TypeError, match="does not implement `handle_async\\(\\)`"): make_async_client(middleware=[SyncOnlyMiddleware()]) def test_construction_rejects_sync_function(self) -> None: def sync_fn(request: APIRequest, call_next: CallNext) -> Any: return call_next(request) with pytest.raises(TypeError, match="is not an async function"): make_async_client(middleware=[sync_fn]) def test_construction_accepts_async_middleware(self) -> None: async def async_fn(request: APIRequest, call_next: AsyncCallNext) -> Any: return await call_next(request) client = make_async_client(middleware=[AsyncOnlyMiddleware(), RecordingMiddleware(), async_fn]) assert len(client._middleware) == 3 class TestMiddlewareProperty: def test_exposes_configured_middleware(self) -> None: recorder = RecordingMiddleware() assert make_sync_client(middleware=[recorder]).middleware == (recorder,) assert make_sync_client().middleware == () @pytest.mark.respx(base_url=base_url) def test_with_options_append_idiom(self, respx_mock: MockRouter) -> None: respx_mock.post("/v1/messages").mock(return_value=httpx.Response(200, json=message_body())) events: list[str] = [] outer = RecordingMiddleware("outer", events) inner = RecordingMiddleware("inner", events) client = make_sync_client(middleware=[outer]) derived = client.with_options(middleware=[*client.middleware, inner]) message = derived.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(message, Message) assert events == ["outer:enter", "inner:enter", "inner:exit", "outer:exit"] # the parent client is unchanged assert client.middleware == (outer,) def make_bedrock_client(**kwargs: Any) -> AnthropicBedrock: return AnthropicBedrock(aws_region="us-east-1", api_key="aws-bearer-token", **kwargs) def make_async_bedrock_client(**kwargs: Any) -> AsyncAnthropicBedrock: return AsyncAnthropicBedrock(aws_region="us-east-1", api_key="aws-bearer-token", **kwargs) def make_vertex_client(**kwargs: Any) -> AnthropicVertex: return AnthropicVertex(region="region", project_id="project", access_token="my-access-token", **kwargs) def make_async_vertex_client(**kwargs: Any) -> AsyncAnthropicVertex: return AsyncAnthropicVertex(region="region", project_id="project", access_token="my-access-token", **kwargs) def make_mantle_client(**kwargs: Any) -> AnthropicBedrockMantle: return AnthropicBedrockMantle(aws_region="us-east-1", api_key="aws-bearer-token", **kwargs) def make_async_mantle_client(**kwargs: Any) -> AsyncAnthropicBedrockMantle: return AsyncAnthropicBedrockMantle(aws_region="us-east-1", api_key="aws-bearer-token", **kwargs) class TestLibClientMiddleware: bedrock_url = re.compile(r"https://bedrock-runtime\.us-east-1\.amazonaws\.com/model/.*/invoke") vertex_url = ( "https://region-aiplatform.googleapis.com/v1" "/projects/project/locations/region/publishers/anthropic/models/claude-opus-4-6:rawPredict" ) mantle_url = "https://bedrock-mantle.us-east-1.api.aws/anthropic/v1/messages" @pytest.mark.respx() def test_bedrock_middleware_sees_canonical_request(self, respx_mock: MockRouter) -> None: respx_mock.post(self.bedrock_url).mock(return_value=httpx.Response(200, json=message_body())) recorder = RecordingMiddleware() client = make_bedrock_client(middleware=[recorder]) message = client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(message, Message) # the middleware sees the canonical request shape, before backend rewriting request = recorder.requests[0] assert request.url == "/v1/messages" assert middleware_request_body(request)["model"] == "claude-opus-4-6" # while the wire request hits the rewritten Bedrock URL with the model moved out of the body wire = cast("MockRequestCall", respx_mock.calls[0]).request assert wire.url.path == "/model/claude-opus-4-6/invoke" wire_body = json.loads(wire.content) assert "model" not in wire_body assert wire_body["anthropic_version"] == "bedrock-2023-05-31" @pytest.mark.respx() async def test_async_bedrock_middleware_sees_canonical_request(self, respx_mock: MockRouter) -> None: respx_mock.post(self.bedrock_url).mock(return_value=httpx.Response(200, json=message_body())) recorder = RecordingMiddleware() client = make_async_bedrock_client(middleware=[recorder]) message = await client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(message, Message) request = recorder.requests[0] assert request.url == "/v1/messages" assert middleware_request_body(request)["model"] == "claude-opus-4-6" wire = cast("MockRequestCall", respx_mock.calls[0]).request assert wire.url.path == "/model/claude-opus-4-6/invoke" @pytest.mark.respx() def test_vertex_middleware_sees_canonical_request(self, respx_mock: MockRouter) -> None: respx_mock.post(self.vertex_url).mock(return_value=httpx.Response(200, json=message_body())) recorder = RecordingMiddleware() client = make_vertex_client(middleware=[recorder]) message = client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(message, Message) request = recorder.requests[0] assert request.url == "/v1/messages" assert middleware_request_body(request)["model"] == "claude-opus-4-6" wire = cast("MockRequestCall", respx_mock.calls[0]).request wire_body = json.loads(wire.content) assert "model" not in wire_body assert wire_body["anthropic_version"] == "vertex-2023-10-16" @pytest.mark.respx() async def test_async_vertex_middleware_sees_canonical_request(self, respx_mock: MockRouter) -> None: respx_mock.post(self.vertex_url).mock(return_value=httpx.Response(200, json=message_body())) recorder = RecordingMiddleware() client = make_async_vertex_client(middleware=[recorder]) message = await client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(message, Message) request = recorder.requests[0] assert request.url == "/v1/messages" assert middleware_request_body(request)["model"] == "claude-opus-4-6" @pytest.mark.respx() def test_mantle_middleware_sees_canonical_request(self, respx_mock: MockRouter) -> None: respx_mock.post(self.mantle_url).mock(return_value=httpx.Response(200, json=message_body())) recorder = RecordingMiddleware() client = make_mantle_client(middleware=[recorder]) message = client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(message, Message) request = recorder.requests[0] assert request.url == "/v1/messages" assert middleware_request_body(request)["model"] == "claude-opus-4-6" wire = cast("MockRequestCall", respx_mock.calls[0]).request assert wire.url.path == "/anthropic/v1/messages" @pytest.mark.respx() async def test_async_mantle_middleware_sees_canonical_request(self, respx_mock: MockRouter) -> None: respx_mock.post(self.mantle_url).mock(return_value=httpx.Response(200, json=message_body())) recorder = RecordingMiddleware() client = make_async_mantle_client(middleware=[recorder]) message = await client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(message, Message) request = recorder.requests[0] assert request.url == "/v1/messages" assert middleware_request_body(request)["model"] == "claude-opus-4-6" wire = cast("MockRequestCall", respx_mock.calls[0]).request assert wire.url.path == "/anthropic/v1/messages" @pytest.mark.respx() def test_bedrock_fallback_middleware_retries_with_new_model(self, respx_mock: MockRouter) -> None: respx_mock.post(self.bedrock_url).mock( side_effect=[ httpx.Response(529, json=error_body(type="overloaded_error", message="Overloaded")), httpx.Response(200, json=message_body(model="claude-sonnet-4-5")), ] ) class ModelFallback(Middleware): @override def handle(self, request: APIRequest, call_next: CallNext) -> Any: response = call_next(request) if response.status_code != 529: return response fallback = request.copy( body={**middleware_request_body(request), "model": "claude-sonnet-4-5"}, ) return call_next(fallback) client = make_bedrock_client(middleware=[ModelFallback()], max_retries=0) message = client.messages.create( max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], model="claude-opus-4-6", ) assert isinstance(message, Message) assert message.model == "claude-sonnet-4-5" # the fallback model is reflected in the rewritten URL of the second wire request first = cast("MockRequestCall", respx_mock.calls[0]).request second = cast("MockRequestCall", respx_mock.calls[1]).request assert first.url.path == "/model/claude-opus-4-6/invoke" assert second.url.path == "/model/claude-sonnet-4-5/invoke" def test_foundry_clients_accept_middleware(self) -> None: recorder = RecordingMiddleware() client = AnthropicFoundry(api_key=api_key, resource="resource", middleware=[recorder]) assert client.middleware == (recorder,) async_client = AsyncAnthropicFoundry(api_key=api_key, resource="resource", middleware=[recorder]) assert async_client.middleware == (recorder,) def test_lib_clients_copy_inherits_replaces_and_clears(self) -> None: recorder = RecordingMiddleware() clients = [ make_bedrock_client(middleware=[recorder]), make_vertex_client(middleware=[recorder]), make_mantle_client(middleware=[recorder]), AnthropicFoundry(api_key=api_key, resource="resource", middleware=[recorder]), AnthropicAWS(skip_auth=True, base_url=base_url, middleware=[recorder]), ] for client in clients: assert client.middleware == (recorder,) assert client.copy().middleware == (recorder,) assert client.with_options(timeout=5).middleware == (recorder,) other = RecordingMiddleware() assert client.copy(middleware=[other]).middleware == (other,) assert client.copy(middleware=None).middleware == () assert client.with_middleware(other).middleware == (recorder, other) assert client.middleware == (recorder,) def test_async_lib_clients_copy_inherits_replaces_and_clears(self) -> None: recorder = RecordingMiddleware() clients = [ make_async_bedrock_client(middleware=[recorder]), make_async_vertex_client(middleware=[recorder]), make_async_mantle_client(middleware=[recorder]), AsyncAnthropicFoundry(api_key=api_key, resource="resource", middleware=[recorder]), AsyncAnthropicAWS(skip_auth=True, base_url=base_url, middleware=[recorder]), ] for client in clients: assert client.middleware == (recorder,) assert client.copy().middleware == (recorder,) assert client.with_options(timeout=5).middleware == (recorder,) other = RecordingMiddleware() assert client.copy(middleware=[other]).middleware == (other,) assert client.copy(middleware=None).middleware == () assert client.with_middleware(other).middleware == (recorder, other) assert client.middleware == (recorder,) def test_lib_client_construction_validates_middleware(self) -> None: def sync_fn(request: APIRequest, call_next: CallNext) -> Any: return call_next(request) with pytest.raises(TypeError, match="is not an async function"): make_async_bedrock_client(middleware=[sync_fn]) with pytest.raises(TypeError, match="is not an async function"): make_async_mantle_client(middleware=[sync_fn]) async def async_fn(request: APIRequest, call_next: AsyncCallNext) -> Any: return await call_next(request) with pytest.raises(TypeError, match="is an async function"): make_vertex_client(middleware=[async_fn]) with pytest.raises(TypeError, match="is an async function"): make_mantle_client(middleware=[async_fn]) anthropic-sdk-python-0.120.2/tests/test_models.py000066400000000000000000000714361523216435200220150ustar00rootroot00000000000000import json from typing import TYPE_CHECKING, Any, Dict, List, Union, Iterable, Optional, cast from datetime import datetime, timezone from collections import deque from typing_extensions import Literal, Annotated, TypedDict, TypeAliasType import pytest import pydantic from pydantic import Field from anthropic._utils import PropertyInfo from anthropic._compat import PYDANTIC_V1, parse_obj, model_dump, model_json from anthropic._models import DISCRIMINATOR_CACHE, BaseModel, EagerIterable, construct_type class BasicModel(BaseModel): foo: str @pytest.mark.parametrize("value", ["hello", 1], ids=["correct type", "mismatched"]) def test_basic(value: object) -> None: m = BasicModel.construct(foo=value) assert m.foo == value def test_directly_nested_model() -> None: class NestedModel(BaseModel): nested: BasicModel m = NestedModel.construct(nested={"foo": "Foo!"}) assert m.nested.foo == "Foo!" # mismatched types m = NestedModel.construct(nested="hello!") assert cast(Any, m.nested) == "hello!" def test_optional_nested_model() -> None: class NestedModel(BaseModel): nested: Optional[BasicModel] m1 = NestedModel.construct(nested=None) assert m1.nested is None m2 = NestedModel.construct(nested={"foo": "bar"}) assert m2.nested is not None assert m2.nested.foo == "bar" # mismatched types m3 = NestedModel.construct(nested={"foo"}) assert isinstance(cast(Any, m3.nested), set) assert cast(Any, m3.nested) == {"foo"} def test_list_nested_model() -> None: class NestedModel(BaseModel): nested: List[BasicModel] m = NestedModel.construct(nested=[{"foo": "bar"}, {"foo": "2"}]) assert m.nested is not None assert isinstance(m.nested, list) assert len(m.nested) == 2 assert m.nested[0].foo == "bar" assert m.nested[1].foo == "2" # mismatched types m = NestedModel.construct(nested=True) assert cast(Any, m.nested) is True m = NestedModel.construct(nested=[False]) assert cast(Any, m.nested) == [False] def test_optional_list_nested_model() -> None: class NestedModel(BaseModel): nested: Optional[List[BasicModel]] m1 = NestedModel.construct(nested=[{"foo": "bar"}, {"foo": "2"}]) assert m1.nested is not None assert isinstance(m1.nested, list) assert len(m1.nested) == 2 assert m1.nested[0].foo == "bar" assert m1.nested[1].foo == "2" m2 = NestedModel.construct(nested=None) assert m2.nested is None # mismatched types m3 = NestedModel.construct(nested={1}) assert cast(Any, m3.nested) == {1} m4 = NestedModel.construct(nested=[False]) assert cast(Any, m4.nested) == [False] def test_list_optional_items_nested_model() -> None: class NestedModel(BaseModel): nested: List[Optional[BasicModel]] m = NestedModel.construct(nested=[None, {"foo": "bar"}]) assert m.nested is not None assert isinstance(m.nested, list) assert len(m.nested) == 2 assert m.nested[0] is None assert m.nested[1] is not None assert m.nested[1].foo == "bar" # mismatched types m3 = NestedModel.construct(nested="foo") assert cast(Any, m3.nested) == "foo" m4 = NestedModel.construct(nested=[False]) assert cast(Any, m4.nested) == [False] def test_list_mismatched_type() -> None: class NestedModel(BaseModel): nested: List[str] m = NestedModel.construct(nested=False) assert cast(Any, m.nested) is False def test_raw_dictionary() -> None: class NestedModel(BaseModel): nested: Dict[str, str] m = NestedModel.construct(nested={"hello": "world"}) assert m.nested == {"hello": "world"} # mismatched types m = NestedModel.construct(nested=False) assert cast(Any, m.nested) is False def test_nested_dictionary_model() -> None: class NestedModel(BaseModel): nested: Dict[str, BasicModel] m = NestedModel.construct(nested={"hello": {"foo": "bar"}}) assert isinstance(m.nested, dict) assert m.nested["hello"].foo == "bar" # mismatched types m = NestedModel.construct(nested={"hello": False}) assert cast(Any, m.nested["hello"]) is False def test_unknown_fields() -> None: m1 = BasicModel.construct(foo="foo", unknown=1) assert m1.foo == "foo" assert cast(Any, m1).unknown == 1 m2 = BasicModel.construct(foo="foo", unknown={"foo_bar": True}) assert m2.foo == "foo" assert cast(Any, m2).unknown == {"foo_bar": True} assert model_dump(m2) == {"foo": "foo", "unknown": {"foo_bar": True}} def test_strict_validation_unknown_fields() -> None: class Model(BaseModel): foo: str model = parse_obj(Model, dict(foo="hello!", user="Robert")) assert model.foo == "hello!" assert cast(Any, model).user == "Robert" assert model_dump(model) == {"foo": "hello!", "user": "Robert"} def test_aliases() -> None: class Model(BaseModel): my_field: int = Field(alias="myField") m = Model.construct(myField=1) assert m.my_field == 1 # mismatched types m = Model.construct(myField={"hello": False}) assert cast(Any, m.my_field) == {"hello": False} def test_repr() -> None: model = BasicModel(foo="bar") assert str(model) == "BasicModel(foo='bar')" assert repr(model) == "BasicModel(foo='bar')" def test_repr_nested_model() -> None: class Child(BaseModel): name: str age: int class Parent(BaseModel): name: str child: Child model = Parent(name="Robert", child=Child(name="Foo", age=5)) assert str(model) == "Parent(name='Robert', child=Child(name='Foo', age=5))" assert repr(model) == "Parent(name='Robert', child=Child(name='Foo', age=5))" def test_optional_list() -> None: class Submodel(BaseModel): name: str class Model(BaseModel): items: Optional[List[Submodel]] m = Model.construct(items=None) assert m.items is None m = Model.construct(items=[]) assert m.items == [] m = Model.construct(items=[{"name": "Robert"}]) assert m.items is not None assert len(m.items) == 1 assert m.items[0].name == "Robert" def test_nested_union_of_models() -> None: class Submodel1(BaseModel): bar: bool class Submodel2(BaseModel): thing: str class Model(BaseModel): foo: Union[Submodel1, Submodel2] m = Model.construct(foo={"thing": "hello"}) assert isinstance(m.foo, Submodel2) assert m.foo.thing == "hello" def test_nested_union_of_mixed_types() -> None: class Submodel1(BaseModel): bar: bool class Model(BaseModel): foo: Union[Submodel1, Literal[True], Literal["CARD_HOLDER"]] m = Model.construct(foo=True) assert m.foo is True m = Model.construct(foo="CARD_HOLDER") assert m.foo == "CARD_HOLDER" m = Model.construct(foo={"bar": False}) assert isinstance(m.foo, Submodel1) assert m.foo.bar is False def test_nested_union_multiple_variants() -> None: class Submodel1(BaseModel): bar: bool class Submodel2(BaseModel): thing: str class Submodel3(BaseModel): foo: int class Model(BaseModel): foo: Union[Submodel1, Submodel2, None, Submodel3] m = Model.construct(foo={"thing": "hello"}) assert isinstance(m.foo, Submodel2) assert m.foo.thing == "hello" m = Model.construct(foo=None) assert m.foo is None m = Model.construct() assert m.foo is None m = Model.construct(foo={"foo": "1"}) assert isinstance(m.foo, Submodel3) assert m.foo.foo == 1 def test_nested_union_invalid_data() -> None: class Submodel1(BaseModel): level: int class Submodel2(BaseModel): name: str class Model(BaseModel): foo: Union[Submodel1, Submodel2] m = Model.construct(foo=True) assert cast(bool, m.foo) is True m = Model.construct(foo={"name": 3}) if PYDANTIC_V1: assert isinstance(m.foo, Submodel2) assert m.foo.name == "3" else: assert isinstance(m.foo, Submodel1) assert m.foo.name == 3 # type: ignore def test_list_of_unions() -> None: class Submodel1(BaseModel): level: int class Submodel2(BaseModel): name: str class Model(BaseModel): items: List[Union[Submodel1, Submodel2]] m = Model.construct(items=[{"level": 1}, {"name": "Robert"}]) assert len(m.items) == 2 assert isinstance(m.items[0], Submodel1) assert m.items[0].level == 1 assert isinstance(m.items[1], Submodel2) assert m.items[1].name == "Robert" m = Model.construct(items=[{"level": -1}, 156]) assert len(m.items) == 2 assert isinstance(m.items[0], Submodel1) assert m.items[0].level == -1 assert cast(Any, m.items[1]) == 156 def test_union_of_lists() -> None: class SubModel1(BaseModel): level: int class SubModel2(BaseModel): name: str class Model(BaseModel): items: Union[List[SubModel1], List[SubModel2]] # with one valid entry m = Model.construct(items=[{"name": "Robert"}]) assert len(m.items) == 1 assert isinstance(m.items[0], SubModel2) assert m.items[0].name == "Robert" # with two entries pointing to different types m = Model.construct(items=[{"level": 1}, {"name": "Robert"}]) assert len(m.items) == 2 assert isinstance(m.items[0], SubModel1) assert m.items[0].level == 1 assert isinstance(m.items[1], SubModel1) assert cast(Any, m.items[1]).name == "Robert" # with two entries pointing to *completely* different types m = Model.construct(items=[{"level": -1}, 156]) assert len(m.items) == 2 assert isinstance(m.items[0], SubModel1) assert m.items[0].level == -1 assert cast(Any, m.items[1]) == 156 def test_dict_of_union() -> None: class SubModel1(BaseModel): name: str class SubModel2(BaseModel): foo: str class Model(BaseModel): data: Dict[str, Union[SubModel1, SubModel2]] m = Model.construct(data={"hello": {"name": "there"}, "foo": {"foo": "bar"}}) assert len(list(m.data.keys())) == 2 assert isinstance(m.data["hello"], SubModel1) assert m.data["hello"].name == "there" assert isinstance(m.data["foo"], SubModel2) assert m.data["foo"].foo == "bar" # TODO: test mismatched type def test_double_nested_union() -> None: class SubModel1(BaseModel): name: str class SubModel2(BaseModel): bar: str class Model(BaseModel): data: Dict[str, List[Union[SubModel1, SubModel2]]] m = Model.construct(data={"foo": [{"bar": "baz"}, {"name": "Robert"}]}) assert len(m.data["foo"]) == 2 entry1 = m.data["foo"][0] assert isinstance(entry1, SubModel2) assert entry1.bar == "baz" entry2 = m.data["foo"][1] assert isinstance(entry2, SubModel1) assert entry2.name == "Robert" # TODO: test mismatched type def test_union_of_dict() -> None: class SubModel1(BaseModel): name: str class SubModel2(BaseModel): foo: str class Model(BaseModel): data: Union[Dict[str, SubModel1], Dict[str, SubModel2]] m = Model.construct(data={"hello": {"name": "there"}, "foo": {"foo": "bar"}}) assert len(list(m.data.keys())) == 2 assert isinstance(m.data["hello"], SubModel1) assert m.data["hello"].name == "there" assert isinstance(m.data["foo"], SubModel1) assert cast(Any, m.data["foo"]).foo == "bar" def test_iso8601_datetime() -> None: class Model(BaseModel): created_at: datetime expected = datetime(2019, 12, 27, 18, 11, 19, 117000, tzinfo=timezone.utc) if PYDANTIC_V1: expected_json = '{"created_at": "2019-12-27T18:11:19.117000+00:00"}' else: expected_json = '{"created_at":"2019-12-27T18:11:19.117000Z"}' model = Model.construct(created_at="2019-12-27T18:11:19.117Z") assert model.created_at == expected assert model_json(model) == expected_json model = parse_obj(Model, dict(created_at="2019-12-27T18:11:19.117Z")) assert model.created_at == expected assert model_json(model) == expected_json def test_does_not_coerce_int() -> None: class Model(BaseModel): bar: int assert Model.construct(bar=1).bar == 1 assert Model.construct(bar=10.9).bar == 10.9 assert Model.construct(bar="19").bar == "19" # type: ignore[comparison-overlap] assert Model.construct(bar=False).bar is False def test_int_to_float_safe_conversion() -> None: class Model(BaseModel): float_field: float m = Model.construct(float_field=10) assert m.float_field == 10.0 assert isinstance(m.float_field, float) m = Model.construct(float_field=10.12) assert m.float_field == 10.12 assert isinstance(m.float_field, float) # number too big m = Model.construct(float_field=2**53 + 1) assert m.float_field == 2**53 + 1 assert isinstance(m.float_field, int) def test_deprecated_alias() -> None: class Model(BaseModel): resource_id: str = Field(alias="model_id") @property def model_id(self) -> str: return self.resource_id m = Model.construct(model_id="id") assert m.model_id == "id" assert m.resource_id == "id" assert m.resource_id is m.model_id m = parse_obj(Model, {"model_id": "id"}) assert m.model_id == "id" assert m.resource_id == "id" assert m.resource_id is m.model_id def test_omitted_fields() -> None: class Model(BaseModel): resource_id: Optional[str] = None m = Model.construct() assert m.resource_id is None assert "resource_id" not in m.model_fields_set m = Model.construct(resource_id=None) assert m.resource_id is None assert "resource_id" in m.model_fields_set m = Model.construct(resource_id="foo") assert m.resource_id == "foo" assert "resource_id" in m.model_fields_set def test_to_dict() -> None: class Model(BaseModel): foo: Optional[str] = Field(alias="FOO", default=None) m = Model(FOO="hello") assert m.to_dict() == {"FOO": "hello"} assert m.to_dict(use_api_names=False) == {"foo": "hello"} m2 = Model() assert m2.to_dict() == {} assert m2.to_dict(exclude_unset=False) == {"FOO": None} assert m2.to_dict(exclude_unset=False, exclude_none=True) == {} assert m2.to_dict(exclude_unset=False, exclude_defaults=True) == {} m3 = Model(FOO=None) assert m3.to_dict() == {"FOO": None} assert m3.to_dict(exclude_none=True) == {} assert m3.to_dict(exclude_defaults=True) == {} class Model2(BaseModel): created_at: datetime time_str = "2024-03-21T11:39:01.275859" m4 = Model2.construct(created_at=time_str) assert m4.to_dict(mode="python") == {"created_at": datetime.fromisoformat(time_str)} assert m4.to_dict(mode="json") == {"created_at": time_str} if PYDANTIC_V1: with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"): m.to_dict(warnings=False) def test_forwards_compat_model_dump_method() -> None: class Model(BaseModel): foo: Optional[str] = Field(alias="FOO", default=None) m = Model(FOO="hello") assert m.model_dump() == {"foo": "hello"} assert m.model_dump(include={"bar"}) == {} assert m.model_dump(exclude={"foo"}) == {} assert m.model_dump(by_alias=True) == {"FOO": "hello"} m2 = Model() assert m2.model_dump() == {"foo": None} assert m2.model_dump(exclude_unset=True) == {} assert m2.model_dump(exclude_none=True) == {} assert m2.model_dump(exclude_defaults=True) == {} m3 = Model(FOO=None) assert m3.model_dump() == {"foo": None} assert m3.model_dump(exclude_none=True) == {} if PYDANTIC_V1: with pytest.raises(ValueError, match="round_trip is only supported in Pydantic v2"): m.model_dump(round_trip=True) with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"): m.model_dump(warnings=False) def test_compat_method_no_error_for_warnings() -> None: class Model(BaseModel): foo: Optional[str] m = Model(foo="hello") assert isinstance(model_dump(m, warnings=False), dict) def test_to_json() -> None: class Model(BaseModel): foo: Optional[str] = Field(alias="FOO", default=None) m = Model(FOO="hello") assert json.loads(m.to_json()) == {"FOO": "hello"} assert json.loads(m.to_json(use_api_names=False)) == {"foo": "hello"} if PYDANTIC_V1: assert m.to_json(indent=None) == '{"FOO": "hello"}' else: assert m.to_json(indent=None) == '{"FOO":"hello"}' m2 = Model() assert json.loads(m2.to_json()) == {} assert json.loads(m2.to_json(exclude_unset=False)) == {"FOO": None} assert json.loads(m2.to_json(exclude_unset=False, exclude_none=True)) == {} assert json.loads(m2.to_json(exclude_unset=False, exclude_defaults=True)) == {} m3 = Model(FOO=None) assert json.loads(m3.to_json()) == {"FOO": None} assert json.loads(m3.to_json(exclude_none=True)) == {} if PYDANTIC_V1: with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"): m.to_json(warnings=False) def test_forwards_compat_model_dump_json_method() -> None: class Model(BaseModel): foo: Optional[str] = Field(alias="FOO", default=None) m = Model(FOO="hello") assert json.loads(m.model_dump_json()) == {"foo": "hello"} assert json.loads(m.model_dump_json(include={"bar"})) == {} assert json.loads(m.model_dump_json(include={"foo"})) == {"foo": "hello"} assert json.loads(m.model_dump_json(by_alias=True)) == {"FOO": "hello"} assert m.model_dump_json(indent=2) == '{\n "foo": "hello"\n}' m2 = Model() assert json.loads(m2.model_dump_json()) == {"foo": None} assert json.loads(m2.model_dump_json(exclude_unset=True)) == {} assert json.loads(m2.model_dump_json(exclude_none=True)) == {} assert json.loads(m2.model_dump_json(exclude_defaults=True)) == {} m3 = Model(FOO=None) assert json.loads(m3.model_dump_json()) == {"foo": None} assert json.loads(m3.model_dump_json(exclude_none=True)) == {} if PYDANTIC_V1: with pytest.raises(ValueError, match="round_trip is only supported in Pydantic v2"): m.model_dump_json(round_trip=True) with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"): m.model_dump_json(warnings=False) def test_type_compat() -> None: # our model type can be assigned to Pydantic's model type def takes_pydantic(model: pydantic.BaseModel) -> None: # noqa: ARG001 ... class OurModel(BaseModel): foo: Optional[str] = None takes_pydantic(OurModel()) def test_annotated_types() -> None: class Model(BaseModel): value: str m = construct_type( value={"value": "foo"}, type_=cast(Any, Annotated[Model, "random metadata"]), ) assert isinstance(m, Model) assert m.value == "foo" def test_discriminated_unions_invalid_data() -> None: class A(BaseModel): type: Literal["a"] data: str class B(BaseModel): type: Literal["b"] data: int m = construct_type( value={"type": "b", "data": "foo"}, type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="type")]), ) assert isinstance(m, B) assert m.type == "b" assert m.data == "foo" # type: ignore[comparison-overlap] m = construct_type( value={"type": "a", "data": 100}, type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="type")]), ) assert isinstance(m, A) assert m.type == "a" if PYDANTIC_V1: # pydantic v1 automatically converts inputs to strings # if the expected type is a str assert m.data == "100" else: assert m.data == 100 # type: ignore[comparison-overlap] def test_discriminated_unions_unknown_variant() -> None: class A(BaseModel): type: Literal["a"] data: str class B(BaseModel): type: Literal["b"] data: int m = construct_type( value={"type": "c", "data": None, "new_thing": "bar"}, type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="type")]), ) # just chooses the first variant assert isinstance(m, A) assert m.type == "c" # type: ignore[comparison-overlap] assert m.data == None # type: ignore[unreachable] assert m.new_thing == "bar" def test_discriminated_unions_invalid_data_nested_unions() -> None: class A(BaseModel): type: Literal["a"] data: str class B(BaseModel): type: Literal["b"] data: int class C(BaseModel): type: Literal["c"] data: bool m = construct_type( value={"type": "b", "data": "foo"}, type_=cast(Any, Annotated[Union[Union[A, B], C], PropertyInfo(discriminator="type")]), ) assert isinstance(m, B) assert m.type == "b" assert m.data == "foo" # type: ignore[comparison-overlap] m = construct_type( value={"type": "c", "data": "foo"}, type_=cast(Any, Annotated[Union[Union[A, B], C], PropertyInfo(discriminator="type")]), ) assert isinstance(m, C) assert m.type == "c" assert m.data == "foo" # type: ignore[comparison-overlap] def test_discriminated_unions_with_aliases_invalid_data() -> None: class A(BaseModel): foo_type: Literal["a"] = Field(alias="type") data: str class B(BaseModel): foo_type: Literal["b"] = Field(alias="type") data: int m = construct_type( value={"type": "b", "data": "foo"}, type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="foo_type")]), ) assert isinstance(m, B) assert m.foo_type == "b" assert m.data == "foo" # type: ignore[comparison-overlap] m = construct_type( value={"type": "a", "data": 100}, type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="foo_type")]), ) assert isinstance(m, A) assert m.foo_type == "a" if PYDANTIC_V1: # pydantic v1 automatically converts inputs to strings # if the expected type is a str assert m.data == "100" else: assert m.data == 100 # type: ignore[comparison-overlap] def test_discriminated_unions_overlapping_discriminators_invalid_data() -> None: class A(BaseModel): type: Literal["a"] data: bool class B(BaseModel): type: Literal["a"] data: int m = construct_type( value={"type": "a", "data": "foo"}, type_=cast(Any, Annotated[Union[A, B], PropertyInfo(discriminator="type")]), ) assert isinstance(m, B) assert m.type == "a" assert m.data == "foo" # type: ignore[comparison-overlap] def test_discriminated_unions_invalid_data_uses_cache() -> None: class A(BaseModel): type: Literal["a"] data: str class B(BaseModel): type: Literal["b"] data: int UnionType = cast(Any, Union[A, B]) assert not DISCRIMINATOR_CACHE.get(UnionType) m = construct_type( value={"type": "b", "data": "foo"}, type_=cast(Any, Annotated[UnionType, PropertyInfo(discriminator="type")]) ) assert isinstance(m, B) assert m.type == "b" assert m.data == "foo" # type: ignore[comparison-overlap] discriminator = DISCRIMINATOR_CACHE.get(UnionType) assert discriminator is not None m = construct_type( value={"type": "b", "data": "foo"}, type_=cast(Any, Annotated[UnionType, PropertyInfo(discriminator="type")]) ) assert isinstance(m, B) assert m.type == "b" assert m.data == "foo" # type: ignore[comparison-overlap] # if the discriminator details object stays the same between invocations then # we hit the cache assert DISCRIMINATOR_CACHE.get(UnionType) is discriminator @pytest.mark.skipif(PYDANTIC_V1, reason="TypeAliasType is not supported in Pydantic v1") def test_type_alias_type() -> None: Alias = TypeAliasType("Alias", str) # pyright: ignore class Model(BaseModel): alias: Alias union: Union[int, Alias] m = construct_type(value={"alias": "foo", "union": "bar"}, type_=Model) assert isinstance(m, Model) assert isinstance(m.alias, str) assert m.alias == "foo" assert isinstance(m.union, str) assert m.union == "bar" @pytest.mark.skipif(PYDANTIC_V1, reason="TypeAliasType is not supported in Pydantic v1") def test_field_named_cls() -> None: class Model(BaseModel): cls: str m = construct_type(value={"cls": "foo"}, type_=Model) assert isinstance(m, Model) assert isinstance(m.cls, str) def test_discriminated_union_case() -> None: class A(BaseModel): type: Literal["a"] data: bool class B(BaseModel): type: Literal["b"] data: List[Union[A, object]] class ModelA(BaseModel): type: Literal["modelA"] data: int class ModelB(BaseModel): type: Literal["modelB"] required: str data: Union[A, B] # when constructing ModelA | ModelB, value data doesn't match ModelB exactly - missing `required` m = construct_type( value={"type": "modelB", "data": {"type": "a", "data": True}}, type_=cast(Any, Annotated[Union[ModelA, ModelB], PropertyInfo(discriminator="type")]), ) assert isinstance(m, ModelB) def test_nested_discriminated_union() -> None: class InnerType1(BaseModel): type: Literal["type_1"] class InnerModel(BaseModel): inner_value: str class InnerType2(BaseModel): type: Literal["type_2"] some_inner_model: InnerModel class Type1(BaseModel): base_type: Literal["base_type_1"] value: Annotated[ Union[ InnerType1, InnerType2, ], PropertyInfo(discriminator="type"), ] class Type2(BaseModel): base_type: Literal["base_type_2"] T = Annotated[ Union[ Type1, Type2, ], PropertyInfo(discriminator="base_type"), ] model = construct_type( type_=T, value={ "base_type": "base_type_1", "value": { "type": "type_2", }, }, ) assert isinstance(model, Type1) assert isinstance(model.value, InnerType2) @pytest.mark.skipif(PYDANTIC_V1, reason="this is only supported in pydantic v2 for now") def test_extra_properties() -> None: class Item(BaseModel): prop: int class Model(BaseModel): __pydantic_extra__: Dict[str, Item] = Field(init=False) # pyright: ignore[reportIncompatibleVariableOverride] other: str if TYPE_CHECKING: def __getattr__(self, attr: str) -> Item: ... model = construct_type( type_=Model, value={ "a": {"prop": 1}, "other": "foo", }, ) assert isinstance(model, Model) assert model.a.prop == 1 assert isinstance(model.a, Item) assert model.other == "foo" # NOTE: Workaround for Pydantic Iterable behavior. # Iterable fields are replaced with a ValidatorIterator and may be consumed # during serialization, which can cause subsequent dumps to return empty data. # See: https://github.com/pydantic/pydantic/issues/9541 @pytest.mark.parametrize( "data, expected_validated", [ ([1, 2, 3], [1, 2, 3]), ((1, 2, 3), (1, 2, 3)), (set([1, 2, 3]), set([1, 2, 3])), (iter([1, 2, 3]), [1, 2, 3]), ([], []), ((x for x in [1, 2, 3]), [1, 2, 3]), (map(lambda x: x, [1, 2, 3]), [1, 2, 3]), (frozenset([1, 2, 3]), frozenset([1, 2, 3])), (deque([1, 2, 3]), deque([1, 2, 3])), ], ids=["list", "tuple", "set", "iterator", "empty", "generator", "map", "frozenset", "deque"], ) @pytest.mark.skipif(PYDANTIC_V1, reason="this is only supported in pydantic v2") def test_iterable_construction(data: Iterable[int], expected_validated: Iterable[int]) -> None: class TypeWithIterable(TypedDict): items: EagerIterable[int] class Model(BaseModel): data: TypeWithIterable m = Model.model_validate({"data": {"items": data}}) assert m.data["items"] == expected_validated # Verify repeated dumps don't lose data (the original bug) assert m.model_dump()["data"]["items"] == list(expected_validated) assert m.model_dump()["data"]["items"] == list(expected_validated) @pytest.mark.skipif(PYDANTIC_V1, reason="this is only supported in pydantic v2") def test_iterable_construction_str_falls_back_to_list() -> None: # str is iterable (over chars), but str(list_of_chars) produces the list's repr # rather than reconstructing a string from items. We special-case str to fall # back to list instead of attempting reconstruction. class TypeWithIterable(TypedDict): items: EagerIterable[str] class Model(BaseModel): data: TypeWithIterable m = Model.model_validate({"data": {"items": "hello"}}) # falls back to list of chars rather than calling str(["h", "e", "l", "l", "o"]) assert m.data["items"] == ["h", "e", "l", "l", "o"] assert m.model_dump()["data"]["items"] == ["h", "e", "l", "l", "o"] anthropic-sdk-python-0.120.2/tests/test_qs.py000066400000000000000000000062031523216435200211430ustar00rootroot00000000000000from typing import Any, cast from functools import partial from urllib.parse import unquote import pytest from anthropic._qs import Querystring, stringify def test_empty() -> None: assert stringify({}) == "" assert stringify({"a": {}}) == "" assert stringify({"a": {"b": {"c": {}}}}) == "" def test_basic() -> None: assert stringify({"a": 1}) == "a=1" assert stringify({"a": "b"}) == "a=b" assert stringify({"a": True}) == "a=true" assert stringify({"a": False}) == "a=false" assert stringify({"a": 1.23456}) == "a=1.23456" assert stringify({"a": None}) == "" @pytest.mark.parametrize("method", ["class", "function"]) def test_nested_dotted(method: str) -> None: if method == "class": serialise = Querystring(nested_format="dots").stringify else: serialise = partial(stringify, nested_format="dots") assert unquote(serialise({"a": {"b": "c"}})) == "a.b=c" assert unquote(serialise({"a": {"b": "c", "d": "e", "f": "g"}})) == "a.b=c&a.d=e&a.f=g" assert unquote(serialise({"a": {"b": {"c": {"d": "e"}}}})) == "a.b.c.d=e" assert unquote(serialise({"a": {"b": True}})) == "a.b=true" def test_nested_brackets() -> None: assert unquote(stringify({"a": {"b": "c"}})) == "a[b]=c" assert unquote(stringify({"a": {"b": "c", "d": "e", "f": "g"}})) == "a[b]=c&a[d]=e&a[f]=g" assert unquote(stringify({"a": {"b": {"c": {"d": "e"}}}})) == "a[b][c][d]=e" assert unquote(stringify({"a": {"b": True}})) == "a[b]=true" @pytest.mark.parametrize("method", ["class", "function"]) def test_array_comma(method: str) -> None: if method == "class": serialise = Querystring(array_format="comma").stringify else: serialise = partial(stringify, array_format="comma") assert unquote(serialise({"in": ["foo", "bar"]})) == "in=foo,bar" assert unquote(serialise({"a": {"b": [True, False]}})) == "a[b]=true,false" assert unquote(serialise({"a": {"b": [True, False, None, True]}})) == "a[b]=true,false,true" def test_array_repeat() -> None: assert unquote(stringify({"in": ["foo", "bar"]})) == "in=foo&in=bar" assert unquote(stringify({"a": {"b": [True, False]}})) == "a[b]=true&a[b]=false" assert unquote(stringify({"a": {"b": [True, False, None, True]}})) == "a[b]=true&a[b]=false&a[b]=true" assert unquote(stringify({"in": ["foo", {"b": {"c": ["d", "e"]}}]})) == "in=foo&in[b][c]=d&in[b][c]=e" @pytest.mark.parametrize("method", ["class", "function"]) def test_array_brackets(method: str) -> None: if method == "class": serialise = Querystring(array_format="brackets").stringify else: serialise = partial(stringify, array_format="brackets") assert unquote(serialise({"in": ["foo", "bar"]})) == "in[]=foo&in[]=bar" assert unquote(serialise({"a": {"b": [True, False]}})) == "a[b][]=true&a[b][]=false" assert unquote(serialise({"a": {"b": [True, False, None, True]}})) == "a[b][]=true&a[b][]=false&a[b][]=true" def test_unknown_array_format() -> None: with pytest.raises(NotImplementedError, match="Unknown array_format value: foo, choose from comma, repeat"): stringify({"a": ["foo", "bar"]}, array_format=cast(Any, "foo")) anthropic-sdk-python-0.120.2/tests/test_required_args.py000066400000000000000000000057641523216435200233670ustar00rootroot00000000000000from __future__ import annotations import pytest from anthropic._utils import required_args def test_too_many_positional_params() -> None: @required_args(["a"]) def foo(a: str | None = None) -> str | None: return a with pytest.raises(TypeError, match=r"foo\(\) takes 1 argument\(s\) but 2 were given"): foo("a", "b") # type: ignore def test_positional_param() -> None: @required_args(["a"]) def foo(a: str | None = None) -> str | None: return a assert foo("a") == "a" assert foo(None) is None assert foo(a="b") == "b" with pytest.raises(TypeError, match="Missing required argument: 'a'"): foo() def test_keyword_only_param() -> None: @required_args(["a"]) def foo(*, a: str | None = None) -> str | None: return a assert foo(a="a") == "a" assert foo(a=None) is None assert foo(a="b") == "b" with pytest.raises(TypeError, match="Missing required argument: 'a'"): foo() def test_multiple_params() -> None: @required_args(["a", "b", "c"]) def foo(a: str = "", *, b: str = "", c: str = "") -> str | None: return f"{a} {b} {c}" assert foo(a="a", b="b", c="c") == "a b c" error_message = r"Missing required arguments.*" with pytest.raises(TypeError, match=error_message): foo() with pytest.raises(TypeError, match=error_message): foo(a="a") with pytest.raises(TypeError, match=error_message): foo(b="b") with pytest.raises(TypeError, match=error_message): foo(c="c") with pytest.raises(TypeError, match=r"Missing required argument: 'a'"): foo(b="a", c="c") with pytest.raises(TypeError, match=r"Missing required argument: 'b'"): foo("a", c="c") def test_multiple_variants() -> None: @required_args(["a"], ["b"]) def foo(*, a: str | None = None, b: str | None = None) -> str | None: return a if a is not None else b assert foo(a="foo") == "foo" assert foo(b="bar") == "bar" assert foo(a=None) is None assert foo(b=None) is None # TODO: this error message could probably be improved with pytest.raises( TypeError, match=r"Missing required arguments; Expected either \('a'\) or \('b'\) arguments to be given", ): foo() def test_multiple_params_multiple_variants() -> None: @required_args(["a", "b"], ["c"]) def foo(*, a: str | None = None, b: str | None = None, c: str | None = None) -> str | None: if a is not None: return a if b is not None: return b return c error_message = r"Missing required arguments; Expected either \('a' and 'b'\) or \('c'\) arguments to be given" with pytest.raises(TypeError, match=error_message): foo(a="foo") with pytest.raises(TypeError, match=error_message): foo(b="bar") with pytest.raises(TypeError, match=error_message): foo() assert foo(a=None, b="bar") == "bar" assert foo(c=None) is None assert foo(c="foo") == "foo" anthropic-sdk-python-0.120.2/tests/test_response.py000066400000000000000000000225701523216435200223630ustar00rootroot00000000000000import json from typing import Any, List, Union, cast from typing_extensions import Annotated import httpx import pytest import pydantic from anthropic import Anthropic, BaseModel, AsyncAnthropic from anthropic._response import ( APIResponse, BaseAPIResponse, AsyncAPIResponse, BinaryAPIResponse, AsyncBinaryAPIResponse, extract_response_type, ) from anthropic._streaming import Stream from anthropic._base_client import FinalRequestOptions class ConcreteBaseAPIResponse(APIResponse[bytes]): ... class ConcreteAPIResponse(APIResponse[List[str]]): ... class ConcreteAsyncAPIResponse(APIResponse[httpx.Response]): ... def test_extract_response_type_direct_classes() -> None: assert extract_response_type(BaseAPIResponse[str]) == str assert extract_response_type(APIResponse[str]) == str assert extract_response_type(AsyncAPIResponse[str]) == str def test_extract_response_type_direct_class_missing_type_arg() -> None: with pytest.raises( RuntimeError, match="Expected type to have a type argument at index 0 but it did not", ): extract_response_type(AsyncAPIResponse) def test_extract_response_type_concrete_subclasses() -> None: assert extract_response_type(ConcreteBaseAPIResponse) == bytes assert extract_response_type(ConcreteAPIResponse) == List[str] assert extract_response_type(ConcreteAsyncAPIResponse) == httpx.Response def test_extract_response_type_binary_response() -> None: assert extract_response_type(BinaryAPIResponse) == bytes assert extract_response_type(AsyncBinaryAPIResponse) == bytes class PydanticModel(pydantic.BaseModel): ... def test_response_parse_mismatched_basemodel(client: Anthropic) -> None: response = APIResponse( raw=httpx.Response(200, content=b"foo"), client=client, stream=False, stream_cls=None, cast_to=str, options=FinalRequestOptions.construct(method="get", url="/foo"), ) with pytest.raises( TypeError, match="Pydantic models must subclass our base model type, e.g. `from anthropic import BaseModel`", ): response.parse(to=PydanticModel) @pytest.mark.asyncio async def test_async_response_parse_mismatched_basemodel(async_client: AsyncAnthropic) -> None: response = AsyncAPIResponse( raw=httpx.Response(200, content=b"foo"), client=async_client, stream=False, stream_cls=None, cast_to=str, options=FinalRequestOptions.construct(method="get", url="/foo"), ) with pytest.raises( TypeError, match="Pydantic models must subclass our base model type, e.g. `from anthropic import BaseModel`", ): await response.parse(to=PydanticModel) def test_response_parse_custom_stream(client: Anthropic) -> None: response = APIResponse( raw=httpx.Response(200, content=b"foo"), client=client, stream=True, stream_cls=None, cast_to=str, options=FinalRequestOptions.construct(method="get", url="/foo"), ) stream = response.parse(to=Stream[int]) assert stream._cast_to == int @pytest.mark.asyncio async def test_async_response_parse_custom_stream(async_client: AsyncAnthropic) -> None: response = AsyncAPIResponse( raw=httpx.Response(200, content=b"foo"), client=async_client, stream=True, stream_cls=None, cast_to=str, options=FinalRequestOptions.construct(method="get", url="/foo"), ) stream = await response.parse(to=Stream[int]) assert stream._cast_to == int class CustomModel(BaseModel): foo: str bar: int def test_response_parse_custom_model(client: Anthropic) -> None: response = APIResponse( raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})), client=client, stream=False, stream_cls=None, cast_to=str, options=FinalRequestOptions.construct(method="get", url="/foo"), ) obj = response.parse(to=CustomModel) assert obj.foo == "hello!" assert obj.bar == 2 @pytest.mark.asyncio async def test_async_response_parse_custom_model(async_client: AsyncAnthropic) -> None: response = AsyncAPIResponse( raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})), client=async_client, stream=False, stream_cls=None, cast_to=str, options=FinalRequestOptions.construct(method="get", url="/foo"), ) obj = await response.parse(to=CustomModel) assert obj.foo == "hello!" assert obj.bar == 2 def test_response_basemodel_request_id(client: Anthropic) -> None: response = APIResponse( raw=httpx.Response( 200, headers={"request-id": "my-req-id"}, content=json.dumps({"foo": "hello!", "bar": 2}), ), client=client, stream=False, stream_cls=None, cast_to=str, options=FinalRequestOptions.construct(method="get", url="/foo"), ) obj = response.parse(to=CustomModel) assert obj._request_id == "my-req-id" assert obj.foo == "hello!" assert obj.bar == 2 assert obj.to_dict() == {"foo": "hello!", "bar": 2} @pytest.mark.asyncio async def test_async_response_basemodel_request_id(client: Anthropic) -> None: response = AsyncAPIResponse( raw=httpx.Response( 200, headers={"request-id": "my-req-id"}, content=json.dumps({"foo": "hello!", "bar": 2}), ), client=client, stream=False, stream_cls=None, cast_to=str, options=FinalRequestOptions.construct(method="get", url="/foo"), ) obj = await response.parse(to=CustomModel) assert obj._request_id == "my-req-id" assert obj.foo == "hello!" assert obj.bar == 2 assert obj.to_dict() == {"foo": "hello!", "bar": 2} def test_response_parse_annotated_type(client: Anthropic) -> None: response = APIResponse( raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})), client=client, stream=False, stream_cls=None, cast_to=str, options=FinalRequestOptions.construct(method="get", url="/foo"), ) obj = response.parse( to=cast("type[CustomModel]", Annotated[CustomModel, "random metadata"]), ) assert obj.foo == "hello!" assert obj.bar == 2 async def test_async_response_parse_annotated_type(async_client: AsyncAnthropic) -> None: response = AsyncAPIResponse( raw=httpx.Response(200, content=json.dumps({"foo": "hello!", "bar": 2})), client=async_client, stream=False, stream_cls=None, cast_to=str, options=FinalRequestOptions.construct(method="get", url="/foo"), ) obj = await response.parse( to=cast("type[CustomModel]", Annotated[CustomModel, "random metadata"]), ) assert obj.foo == "hello!" assert obj.bar == 2 @pytest.mark.parametrize( "content, expected", [ ("false", False), ("true", True), ("False", False), ("True", True), ("TrUe", True), ("FalSe", False), ], ) def test_response_parse_bool(client: Anthropic, content: str, expected: bool) -> None: response = APIResponse( raw=httpx.Response(200, content=content), client=client, stream=False, stream_cls=None, cast_to=str, options=FinalRequestOptions.construct(method="get", url="/foo"), ) result = response.parse(to=bool) assert result is expected @pytest.mark.parametrize( "content, expected", [ ("false", False), ("true", True), ("False", False), ("True", True), ("TrUe", True), ("FalSe", False), ], ) async def test_async_response_parse_bool(client: AsyncAnthropic, content: str, expected: bool) -> None: response = AsyncAPIResponse( raw=httpx.Response(200, content=content), client=client, stream=False, stream_cls=None, cast_to=str, options=FinalRequestOptions.construct(method="get", url="/foo"), ) result = await response.parse(to=bool) assert result is expected class OtherModel(BaseModel): a: str @pytest.mark.parametrize("client", [False], indirect=True) # loose validation def test_response_parse_expect_model_union_non_json_content(client: Anthropic) -> None: response = APIResponse( raw=httpx.Response(200, content=b"foo", headers={"Content-Type": "application/text"}), client=client, stream=False, stream_cls=None, cast_to=str, options=FinalRequestOptions.construct(method="get", url="/foo"), ) obj = response.parse(to=cast(Any, Union[CustomModel, OtherModel])) assert isinstance(obj, str) assert obj == "foo" @pytest.mark.asyncio @pytest.mark.parametrize("async_client", [False], indirect=True) # loose validation async def test_async_response_parse_expect_model_union_non_json_content(async_client: AsyncAnthropic) -> None: response = AsyncAPIResponse( raw=httpx.Response(200, content=b"foo", headers={"Content-Type": "application/text"}), client=async_client, stream=False, stream_cls=None, cast_to=str, options=FinalRequestOptions.construct(method="get", url="/foo"), ) obj = await response.parse(to=cast(Any, Union[CustomModel, OtherModel])) assert isinstance(obj, str) assert obj == "foo" anthropic-sdk-python-0.120.2/tests/test_streaming.py000066400000000000000000000234061523216435200225150ustar00rootroot00000000000000from __future__ import annotations from typing import TypeVar, Iterator, AsyncIterator import httpx import pytest from anthropic import Anthropic, AsyncAnthropic from anthropic._streaming import Stream, AsyncStream, ServerSentEvent from anthropic._exceptions import APIStatusError _T = TypeVar("_T") @pytest.mark.asyncio @pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) async def test_basic(sync: bool, client: Anthropic, async_client: AsyncAnthropic) -> None: def body() -> Iterator[bytes]: yield b"event: completion\n" yield b'data: {"foo":true}\n' yield b"\n" iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) sse = await iter_next(iterator) assert sse.event == "completion" assert sse.json() == {"foo": True} await assert_empty_iter(iterator) @pytest.mark.asyncio @pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) async def test_data_missing_event(sync: bool, client: Anthropic, async_client: AsyncAnthropic) -> None: def body() -> Iterator[bytes]: yield b'data: {"foo":true}\n' yield b"\n" iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) sse = await iter_next(iterator) assert sse.event is None assert sse.json() == {"foo": True} await assert_empty_iter(iterator) @pytest.mark.asyncio @pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) async def test_event_missing_data(sync: bool, client: Anthropic, async_client: AsyncAnthropic) -> None: def body() -> Iterator[bytes]: yield b"event: ping\n" yield b"\n" iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) sse = await iter_next(iterator) assert sse.event == "ping" assert sse.data == "" await assert_empty_iter(iterator) @pytest.mark.asyncio @pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) async def test_multiple_events(sync: bool, client: Anthropic, async_client: AsyncAnthropic) -> None: def body() -> Iterator[bytes]: yield b"event: ping\n" yield b"\n" yield b"event: completion\n" yield b"\n" iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) sse = await iter_next(iterator) assert sse.event == "ping" assert sse.data == "" sse = await iter_next(iterator) assert sse.event == "completion" assert sse.data == "" await assert_empty_iter(iterator) @pytest.mark.asyncio @pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) async def test_multiple_events_with_data(sync: bool, client: Anthropic, async_client: AsyncAnthropic) -> None: def body() -> Iterator[bytes]: yield b"event: ping\n" yield b'data: {"foo":true}\n' yield b"\n" yield b"event: completion\n" yield b'data: {"bar":false}\n' yield b"\n" iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) sse = await iter_next(iterator) assert sse.event == "ping" assert sse.json() == {"foo": True} sse = await iter_next(iterator) assert sse.event == "completion" assert sse.json() == {"bar": False} await assert_empty_iter(iterator) @pytest.mark.asyncio @pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) async def test_multiple_data_lines_with_empty_line(sync: bool, client: Anthropic, async_client: AsyncAnthropic) -> None: def body() -> Iterator[bytes]: yield b"event: ping\n" yield b"data: {\n" yield b'data: "foo":\n' yield b"data: \n" yield b"data:\n" yield b"data: true}\n" yield b"\n\n" iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) sse = await iter_next(iterator) assert sse.event == "ping" assert sse.json() == {"foo": True} assert sse.data == '{\n"foo":\n\n\ntrue}' await assert_empty_iter(iterator) @pytest.mark.asyncio @pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) async def test_data_json_escaped_double_new_line(sync: bool, client: Anthropic, async_client: AsyncAnthropic) -> None: def body() -> Iterator[bytes]: yield b"event: ping\n" yield b'data: {"foo": "my long\\n\\ncontent"}' yield b"\n\n" iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) sse = await iter_next(iterator) assert sse.event == "ping" assert sse.json() == {"foo": "my long\n\ncontent"} await assert_empty_iter(iterator) @pytest.mark.asyncio @pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) async def test_multiple_data_lines(sync: bool, client: Anthropic, async_client: AsyncAnthropic) -> None: def body() -> Iterator[bytes]: yield b"event: ping\n" yield b"data: {\n" yield b'data: "foo":\n' yield b"data: true}\n" yield b"\n\n" iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) sse = await iter_next(iterator) assert sse.event == "ping" assert sse.json() == {"foo": True} await assert_empty_iter(iterator) @pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) async def test_special_new_line_character( sync: bool, client: Anthropic, async_client: AsyncAnthropic, ) -> None: def body() -> Iterator[bytes]: yield b'data: {"content":" culpa"}\n' yield b"\n" yield b'data: {"content":" \xe2\x80\xa8"}\n' yield b"\n" yield b'data: {"content":"foo"}\n' yield b"\n" iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) sse = await iter_next(iterator) assert sse.event is None assert sse.json() == {"content": " culpa"} sse = await iter_next(iterator) assert sse.event is None assert sse.json() == {"content": " 
"} sse = await iter_next(iterator) assert sse.event is None assert sse.json() == {"content": "foo"} await assert_empty_iter(iterator) @pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) async def test_multi_byte_character_multiple_chunks( sync: bool, client: Anthropic, async_client: AsyncAnthropic, ) -> None: def body() -> Iterator[bytes]: yield b'data: {"content":"' # bytes taken from the string 'извеÑтни' and arbitrarily split # so that some multi-byte characters span multiple chunks yield b"\xd0" yield b"\xb8\xd0\xb7\xd0" yield b"\xb2\xd0\xb5\xd1\x81\xd1\x82\xd0\xbd\xd0\xb8" yield b'"}\n' yield b"\n" iterator = make_event_iterator(content=body(), sync=sync, client=client, async_client=async_client) sse = await iter_next(iterator) assert sse.event is None assert sse.json() == {"content": "извеÑтни"} @pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) async def test_error_type( sync: bool, client: Anthropic, async_client: AsyncAnthropic, ) -> None: def body() -> Iterator[bytes]: yield b"event: error\n" yield b'data: {"type": "error", "error": {"type": "overloaded_error", "message": "Overloaded"}}\n\n' iterator = make_stream_iterator(content=body(), sync=sync, client=client, async_client=async_client) with pytest.raises(APIStatusError) as exc_info: await iter_next(iterator) assert exc_info.value.type == "overloaded_error" assert "Overloaded" in str(exc_info.value) def test_isinstance_check(client: Anthropic, async_client: AsyncAnthropic) -> None: async_stream = AsyncStream(cast_to=object, client=async_client, response=httpx.Response(200, content=b"foo")) assert isinstance(async_stream, AsyncStream) sync_stream = Stream(cast_to=object, client=client, response=httpx.Response(200, content=b"foo")) assert isinstance(sync_stream, Stream) async def to_aiter(iter: Iterator[bytes]) -> AsyncIterator[bytes]: for chunk in iter: yield chunk async def iter_next(iter: Iterator[_T] | AsyncIterator[_T]) -> _T: if isinstance(iter, AsyncIterator): return await iter.__anext__() return next(iter) async def assert_empty_iter(iter: Iterator[ServerSentEvent] | AsyncIterator[ServerSentEvent]) -> None: with pytest.raises((StopAsyncIteration, RuntimeError)): await iter_next(iter) def make_event_iterator( content: Iterator[bytes], *, sync: bool, client: Anthropic, async_client: AsyncAnthropic, ) -> Iterator[ServerSentEvent] | AsyncIterator[ServerSentEvent]: if sync: return Stream(cast_to=object, client=client, response=httpx.Response(200, content=content))._iter_events() return AsyncStream( cast_to=object, client=async_client, response=httpx.Response(200, content=to_aiter(content)) )._iter_events() # Unlike make_event_iterator which only parses SSE events using _iter_events(), # this helper uses __stream__() to process the full stream pipeline including # parsing message objects and converting error events into raised exceptions. def make_stream_iterator( content: Iterator[bytes], *, sync: bool, client: Anthropic, async_client: AsyncAnthropic, ) -> AsyncIterator[object] | Iterator[object]: if sync: return Stream( cast_to=object, client=client, response=httpx.Response(200, content=content, request=httpx.Request("GET", "https://example.com")), ).__stream__() return AsyncStream( cast_to=object, client=async_client, response=httpx.Response(200, content=to_aiter(content), request=httpx.Request("GET", "https://example.com")), ).__stream__() anthropic-sdk-python-0.120.2/tests/test_transform.py000066400000000000000000000413251523216435200225370ustar00rootroot00000000000000from __future__ import annotations import io import pathlib from typing import Any, Dict, List, Union, TypeVar, Iterable, Optional, cast from datetime import date, datetime from typing_extensions import Required, Annotated, TypedDict import pytest import pydantic from anthropic._types import Base64FileInput, omit, not_given from anthropic._utils import ( PropertyInfo, transform as _transform, parse_datetime, async_transform as _async_transform, ) from anthropic._compat import PYDANTIC_V1, model_parse from anthropic._models import BaseModel _T = TypeVar("_T") SAMPLE_FILE_PATH = pathlib.Path(__file__).parent.joinpath("sample_file.txt") async def transform( data: _T, expected_type: object, use_async: bool, ) -> _T: if use_async: return await _async_transform(data, expected_type=expected_type) return _transform(data, expected_type=expected_type) parametrize = pytest.mark.parametrize("use_async", [False, True], ids=["sync", "async"]) class Foo1(TypedDict): foo_bar: Annotated[str, PropertyInfo(alias="fooBar")] @parametrize @pytest.mark.asyncio async def test_top_level_alias(use_async: bool) -> None: assert await transform({"foo_bar": "hello"}, expected_type=Foo1, use_async=use_async) == {"fooBar": "hello"} class Foo2(TypedDict): bar: Bar2 class Bar2(TypedDict): this_thing: Annotated[int, PropertyInfo(alias="this__thing")] baz: Annotated[Baz2, PropertyInfo(alias="Baz")] class Baz2(TypedDict): my_baz: Annotated[str, PropertyInfo(alias="myBaz")] @parametrize @pytest.mark.asyncio async def test_recursive_typeddict(use_async: bool) -> None: assert await transform({"bar": {"this_thing": 1}}, Foo2, use_async) == {"bar": {"this__thing": 1}} assert await transform({"bar": {"baz": {"my_baz": "foo"}}}, Foo2, use_async) == {"bar": {"Baz": {"myBaz": "foo"}}} class Foo3(TypedDict): things: List[Bar3] class Bar3(TypedDict): my_field: Annotated[str, PropertyInfo(alias="myField")] @parametrize @pytest.mark.asyncio async def test_list_of_typeddict(use_async: bool) -> None: result = await transform({"things": [{"my_field": "foo"}, {"my_field": "foo2"}]}, Foo3, use_async) assert result == {"things": [{"myField": "foo"}, {"myField": "foo2"}]} class Foo4(TypedDict): foo: Union[Bar4, Baz4] class Bar4(TypedDict): foo_bar: Annotated[str, PropertyInfo(alias="fooBar")] class Baz4(TypedDict): foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")] @parametrize @pytest.mark.asyncio async def test_union_of_typeddict(use_async: bool) -> None: assert await transform({"foo": {"foo_bar": "bar"}}, Foo4, use_async) == {"foo": {"fooBar": "bar"}} assert await transform({"foo": {"foo_baz": "baz"}}, Foo4, use_async) == {"foo": {"fooBaz": "baz"}} assert await transform({"foo": {"foo_baz": "baz", "foo_bar": "bar"}}, Foo4, use_async) == { "foo": {"fooBaz": "baz", "fooBar": "bar"} } class Foo5(TypedDict): foo: Annotated[Union[Bar4, List[Baz4]], PropertyInfo(alias="FOO")] class Bar5(TypedDict): foo_bar: Annotated[str, PropertyInfo(alias="fooBar")] class Baz5(TypedDict): foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")] @parametrize @pytest.mark.asyncio async def test_union_of_list(use_async: bool) -> None: assert await transform({"foo": {"foo_bar": "bar"}}, Foo5, use_async) == {"FOO": {"fooBar": "bar"}} assert await transform( { "foo": [ {"foo_baz": "baz"}, {"foo_baz": "baz"}, ] }, Foo5, use_async, ) == {"FOO": [{"fooBaz": "baz"}, {"fooBaz": "baz"}]} class Foo6(TypedDict): bar: Annotated[str, PropertyInfo(alias="Bar")] @parametrize @pytest.mark.asyncio async def test_includes_unknown_keys(use_async: bool) -> None: assert await transform({"bar": "bar", "baz_": {"FOO": 1}}, Foo6, use_async) == { "Bar": "bar", "baz_": {"FOO": 1}, } class Foo7(TypedDict): bar: Annotated[List[Bar7], PropertyInfo(alias="bAr")] foo: Bar7 class Bar7(TypedDict): foo: str @parametrize @pytest.mark.asyncio async def test_ignores_invalid_input(use_async: bool) -> None: assert await transform({"bar": ""}, Foo7, use_async) == {"bAr": ""} assert await transform({"foo": ""}, Foo7, use_async) == {"foo": ""} class DatetimeDict(TypedDict, total=False): foo: Annotated[datetime, PropertyInfo(format="iso8601")] bar: Annotated[Optional[datetime], PropertyInfo(format="iso8601")] required: Required[Annotated[Optional[datetime], PropertyInfo(format="iso8601")]] list_: Required[Annotated[Optional[List[datetime]], PropertyInfo(format="iso8601")]] union: Annotated[Union[int, datetime], PropertyInfo(format="iso8601")] class DateDict(TypedDict, total=False): foo: Annotated[date, PropertyInfo(format="iso8601")] class DatetimeModel(BaseModel): foo: datetime class DateModel(BaseModel): foo: Optional[date] @parametrize @pytest.mark.asyncio async def test_iso8601_format(use_async: bool) -> None: dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") tz = "+00:00" if PYDANTIC_V1 else "Z" assert await transform({"foo": dt}, DatetimeDict, use_async) == {"foo": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap] assert await transform(DatetimeModel(foo=dt), Any, use_async) == {"foo": "2023-02-23T14:16:36.337692" + tz} # type: ignore[comparison-overlap] dt = dt.replace(tzinfo=None) assert await transform({"foo": dt}, DatetimeDict, use_async) == {"foo": "2023-02-23T14:16:36.337692"} # type: ignore[comparison-overlap] assert await transform(DatetimeModel(foo=dt), Any, use_async) == {"foo": "2023-02-23T14:16:36.337692"} # type: ignore[comparison-overlap] assert await transform({"foo": None}, DateDict, use_async) == {"foo": None} # type: ignore[comparison-overlap] assert await transform(DateModel(foo=None), Any, use_async) == {"foo": None} # type: ignore assert await transform({"foo": date.fromisoformat("2023-02-23")}, DateDict, use_async) == {"foo": "2023-02-23"} # type: ignore[comparison-overlap] assert await transform(DateModel(foo=date.fromisoformat("2023-02-23")), DateDict, use_async) == { "foo": "2023-02-23" } # type: ignore[comparison-overlap] @parametrize @pytest.mark.asyncio async def test_optional_iso8601_format(use_async: bool) -> None: dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") assert await transform({"bar": dt}, DatetimeDict, use_async) == {"bar": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap] assert await transform({"bar": None}, DatetimeDict, use_async) == {"bar": None} @parametrize @pytest.mark.asyncio async def test_required_iso8601_format(use_async: bool) -> None: dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") assert await transform({"required": dt}, DatetimeDict, use_async) == { "required": "2023-02-23T14:16:36.337692+00:00" } # type: ignore[comparison-overlap] assert await transform({"required": None}, DatetimeDict, use_async) == {"required": None} @parametrize @pytest.mark.asyncio async def test_union_datetime(use_async: bool) -> None: dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") assert await transform({"union": dt}, DatetimeDict, use_async) == { # type: ignore[comparison-overlap] "union": "2023-02-23T14:16:36.337692+00:00" } assert await transform({"union": "foo"}, DatetimeDict, use_async) == {"union": "foo"} @parametrize @pytest.mark.asyncio async def test_nested_list_iso6801_format(use_async: bool) -> None: dt1 = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00") dt2 = parse_datetime("2022-01-15T06:34:23Z") assert await transform({"list_": [dt1, dt2]}, DatetimeDict, use_async) == { # type: ignore[comparison-overlap] "list_": ["2023-02-23T14:16:36.337692+00:00", "2022-01-15T06:34:23+00:00"] } @parametrize @pytest.mark.asyncio async def test_datetime_custom_format(use_async: bool) -> None: dt = parse_datetime("2022-01-15T06:34:23Z") result = await transform(dt, Annotated[datetime, PropertyInfo(format="custom", format_template="%H")], use_async) assert result == "06" # type: ignore[comparison-overlap] class DateDictWithRequiredAlias(TypedDict, total=False): required_prop: Required[Annotated[date, PropertyInfo(format="iso8601", alias="prop")]] @parametrize @pytest.mark.asyncio async def test_datetime_with_alias(use_async: bool) -> None: assert await transform({"required_prop": None}, DateDictWithRequiredAlias, use_async) == {"prop": None} # type: ignore[comparison-overlap] assert await transform( {"required_prop": date.fromisoformat("2023-02-23")}, DateDictWithRequiredAlias, use_async ) == {"prop": "2023-02-23"} # type: ignore[comparison-overlap] class MyModel(BaseModel): foo: str @parametrize @pytest.mark.asyncio async def test_pydantic_model_to_dictionary(use_async: bool) -> None: assert cast(Any, await transform(MyModel(foo="hi!"), Any, use_async)) == {"foo": "hi!"} assert cast(Any, await transform(MyModel.construct(foo="hi!"), Any, use_async)) == {"foo": "hi!"} @parametrize @pytest.mark.asyncio async def test_pydantic_empty_model(use_async: bool) -> None: assert cast(Any, await transform(MyModel.construct(), Any, use_async)) == {} @parametrize @pytest.mark.asyncio async def test_pydantic_unknown_field(use_async: bool) -> None: assert cast(Any, await transform(MyModel.construct(my_untyped_field=True), Any, use_async)) == { "my_untyped_field": True } @parametrize @pytest.mark.asyncio async def test_pydantic_mismatched_types(use_async: bool) -> None: model = MyModel.construct(foo=True) if PYDANTIC_V1: params = await transform(model, Any, use_async) else: with pytest.warns(UserWarning): params = await transform(model, Any, use_async) assert cast(Any, params) == {"foo": True} @parametrize @pytest.mark.asyncio async def test_pydantic_mismatched_object_type(use_async: bool) -> None: model = MyModel.construct(foo=MyModel.construct(hello="world")) if PYDANTIC_V1: params = await transform(model, Any, use_async) else: with pytest.warns(UserWarning): params = await transform(model, Any, use_async) assert cast(Any, params) == {"foo": {"hello": "world"}} class ModelNestedObjects(BaseModel): nested: MyModel @parametrize @pytest.mark.asyncio async def test_pydantic_nested_objects(use_async: bool) -> None: model = ModelNestedObjects.construct(nested={"foo": "stainless"}) assert isinstance(model.nested, MyModel) assert cast(Any, await transform(model, Any, use_async)) == {"nested": {"foo": "stainless"}} class ModelWithDefaultField(BaseModel): foo: str with_none_default: Union[str, None] = None with_str_default: str = "foo" @parametrize @pytest.mark.asyncio async def test_pydantic_default_field(use_async: bool) -> None: # should be excluded when defaults are used model = ModelWithDefaultField.construct() assert model.with_none_default is None assert model.with_str_default == "foo" assert cast(Any, await transform(model, Any, use_async)) == {} # should be included when the default value is explicitly given model = ModelWithDefaultField.construct(with_none_default=None, with_str_default="foo") assert model.with_none_default is None assert model.with_str_default == "foo" assert cast(Any, await transform(model, Any, use_async)) == {"with_none_default": None, "with_str_default": "foo"} # should be included when a non-default value is explicitly given model = ModelWithDefaultField.construct(with_none_default="bar", with_str_default="baz") assert model.with_none_default == "bar" assert model.with_str_default == "baz" assert cast(Any, await transform(model, Any, use_async)) == {"with_none_default": "bar", "with_str_default": "baz"} class ModelWithAliasedField(BaseModel): from_: str = pydantic.Field(alias="from") @parametrize @pytest.mark.asyncio async def test_pydantic_aliased_field(use_async: bool) -> None: model = model_parse(ModelWithAliasedField, {"from": "bar"}) assert model.from_ == "bar" assert cast(Any, await transform(model, Any, use_async)) == {"from": "bar"} model = ModelWithAliasedField.construct(**cast("dict[str, Any]", {"from": "bar"})) assert model.from_ == "bar" assert cast(Any, await transform(model, Any, use_async)) == {"from": "bar"} @parametrize @pytest.mark.asyncio async def test_pydantic_aliased_field_round_trip(use_async: bool) -> None: from anthropic.types.beta import BetaMessageParam, BetaFallbackBlock block = model_parse( BetaFallbackBlock, { "from": {"model": "model-a"}, "to": {"model": "model-b"}, "trigger": {"type": "refusal", "category": None}, "type": "fallback", }, ) assert block.from_.model == "model-a" message = cast("BetaMessageParam", {"role": "assistant", "content": [block]}) params = cast(Any, await transform(message, BetaMessageParam, use_async)) assert params["content"][0] == { "from": {"model": "model-a"}, "to": {"model": "model-b"}, "trigger": {"type": "refusal", "category": None}, "type": "fallback", } assert "from_" not in params["content"][0] class TypedDictIterableUnion(TypedDict): foo: Annotated[Union[Bar8, Iterable[Baz8]], PropertyInfo(alias="FOO")] class Bar8(TypedDict): foo_bar: Annotated[str, PropertyInfo(alias="fooBar")] class Baz8(TypedDict): foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")] @parametrize @pytest.mark.asyncio async def test_iterable_of_dictionaries(use_async: bool) -> None: assert await transform({"foo": [{"foo_baz": "bar"}]}, TypedDictIterableUnion, use_async) == { "FOO": [{"fooBaz": "bar"}] } assert cast(Any, await transform({"foo": ({"foo_baz": "bar"},)}, TypedDictIterableUnion, use_async)) == { "FOO": [{"fooBaz": "bar"}] } def my_iter() -> Iterable[Baz8]: yield {"foo_baz": "hello"} yield {"foo_baz": "world"} assert await transform({"foo": my_iter()}, TypedDictIterableUnion, use_async) == { "FOO": [{"fooBaz": "hello"}, {"fooBaz": "world"}] } @parametrize @pytest.mark.asyncio async def test_dictionary_items(use_async: bool) -> None: class DictItems(TypedDict): foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")] assert await transform({"foo": {"foo_baz": "bar"}}, Dict[str, DictItems], use_async) == {"foo": {"fooBaz": "bar"}} class TypedDictIterableUnionStr(TypedDict): foo: Annotated[Union[str, Iterable[Baz8]], PropertyInfo(alias="FOO")] @parametrize @pytest.mark.asyncio async def test_iterable_union_str(use_async: bool) -> None: assert await transform({"foo": "bar"}, TypedDictIterableUnionStr, use_async) == {"FOO": "bar"} assert cast(Any, await transform(iter([{"foo_baz": "bar"}]), Union[str, Iterable[Baz8]], use_async)) == [ {"fooBaz": "bar"} ] class TypedDictBase64Input(TypedDict): foo: Annotated[Union[str, Base64FileInput], PropertyInfo(format="base64")] @parametrize @pytest.mark.asyncio async def test_base64_file_input(use_async: bool) -> None: # strings are left as-is assert await transform({"foo": "bar"}, TypedDictBase64Input, use_async) == {"foo": "bar"} # pathlib.Path is automatically converted to base64 assert await transform({"foo": SAMPLE_FILE_PATH}, TypedDictBase64Input, use_async) == { "foo": "SGVsbG8sIHdvcmxkIQo=" } # type: ignore[comparison-overlap] # io instances are automatically converted to base64 assert await transform({"foo": io.StringIO("Hello, world!")}, TypedDictBase64Input, use_async) == { "foo": "SGVsbG8sIHdvcmxkIQ==" } # type: ignore[comparison-overlap] assert await transform({"foo": io.BytesIO(b"Hello, world!")}, TypedDictBase64Input, use_async) == { "foo": "SGVsbG8sIHdvcmxkIQ==" } # type: ignore[comparison-overlap] @parametrize @pytest.mark.asyncio async def test_transform_skipping(use_async: bool) -> None: # lists of ints are left as-is data = [1, 2, 3] assert await transform(data, List[int], use_async) is data # iterables of ints are converted to a list data = iter([1, 2, 3]) assert await transform(data, Iterable[int], use_async) == [1, 2, 3] @parametrize @pytest.mark.asyncio async def test_strips_notgiven(use_async: bool) -> None: assert await transform({"foo_bar": "bar"}, Foo1, use_async) == {"fooBar": "bar"} assert await transform({"foo_bar": not_given}, Foo1, use_async) == {} @parametrize @pytest.mark.asyncio async def test_strips_omit(use_async: bool) -> None: assert await transform({"foo_bar": "bar"}, Foo1, use_async) == {"fooBar": "bar"} assert await transform({"foo_bar": omit}, Foo1, use_async) == {} anthropic-sdk-python-0.120.2/tests/test_utils/000077500000000000000000000000001523216435200213055ustar00rootroot00000000000000anthropic-sdk-python-0.120.2/tests/test_utils/test_datetime_parse.py000066400000000000000000000125211523216435200257050ustar00rootroot00000000000000""" Copied from https://github.com/pydantic/pydantic/blob/v1.10.22/tests/test_datetime_parse.py with modifications so it works without pydantic v1 imports. """ from typing import Type, Union from datetime import date, datetime, timezone, timedelta import pytest from anthropic._utils import parse_date, parse_datetime def create_tz(minutes: int) -> timezone: return timezone(timedelta(minutes=minutes)) @pytest.mark.parametrize( "value,result", [ # Valid inputs ("1494012444.883309", date(2017, 5, 5)), (b"1494012444.883309", date(2017, 5, 5)), (1_494_012_444.883_309, date(2017, 5, 5)), ("1494012444", date(2017, 5, 5)), (1_494_012_444, date(2017, 5, 5)), (0, date(1970, 1, 1)), ("2012-04-23", date(2012, 4, 23)), (b"2012-04-23", date(2012, 4, 23)), ("2012-4-9", date(2012, 4, 9)), (date(2012, 4, 9), date(2012, 4, 9)), (datetime(2012, 4, 9, 12, 15), date(2012, 4, 9)), # Invalid inputs ("x20120423", ValueError), ("2012-04-56", ValueError), (19_999_999_999, date(2603, 10, 11)), # just before watershed (20_000_000_001, date(1970, 8, 20)), # just after watershed (1_549_316_052, date(2019, 2, 4)), # nowish in s (1_549_316_052_104, date(2019, 2, 4)), # nowish in ms (1_549_316_052_104_324, date(2019, 2, 4)), # nowish in μs (1_549_316_052_104_324_096, date(2019, 2, 4)), # nowish in ns ("infinity", date(9999, 12, 31)), ("inf", date(9999, 12, 31)), (float("inf"), date(9999, 12, 31)), ("infinity ", date(9999, 12, 31)), (int("1" + "0" * 100), date(9999, 12, 31)), (1e1000, date(9999, 12, 31)), ("-infinity", date(1, 1, 1)), ("-inf", date(1, 1, 1)), ("nan", ValueError), ], ) def test_date_parsing(value: Union[str, bytes, int, float], result: Union[date, Type[Exception]]) -> None: if type(result) == type and issubclass(result, Exception): # pyright: ignore[reportUnnecessaryIsInstance] with pytest.raises(result): parse_date(value) else: assert parse_date(value) == result @pytest.mark.parametrize( "value,result", [ # Valid inputs # values in seconds ("1494012444.883309", datetime(2017, 5, 5, 19, 27, 24, 883_309, tzinfo=timezone.utc)), (1_494_012_444.883_309, datetime(2017, 5, 5, 19, 27, 24, 883_309, tzinfo=timezone.utc)), ("1494012444", datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)), (b"1494012444", datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)), (1_494_012_444, datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)), # values in ms ("1494012444000.883309", datetime(2017, 5, 5, 19, 27, 24, 883, tzinfo=timezone.utc)), ("-1494012444000.883309", datetime(1922, 8, 29, 4, 32, 35, 999117, tzinfo=timezone.utc)), (1_494_012_444_000, datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)), ("2012-04-23T09:15:00", datetime(2012, 4, 23, 9, 15)), ("2012-4-9 4:8:16", datetime(2012, 4, 9, 4, 8, 16)), ("2012-04-23T09:15:00Z", datetime(2012, 4, 23, 9, 15, 0, 0, timezone.utc)), ("2012-4-9 4:8:16-0320", datetime(2012, 4, 9, 4, 8, 16, 0, create_tz(-200))), ("2012-04-23T10:20:30.400+02:30", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(150))), ("2012-04-23T10:20:30.400+02", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(120))), ("2012-04-23T10:20:30.400-02", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(-120))), (b"2012-04-23T10:20:30.400-02", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(-120))), (datetime(2017, 5, 5), datetime(2017, 5, 5)), (0, datetime(1970, 1, 1, 0, 0, 0, tzinfo=timezone.utc)), # Invalid inputs ("x20120423091500", ValueError), ("2012-04-56T09:15:90", ValueError), ("2012-04-23T11:05:00-25:00", ValueError), (19_999_999_999, datetime(2603, 10, 11, 11, 33, 19, tzinfo=timezone.utc)), # just before watershed (20_000_000_001, datetime(1970, 8, 20, 11, 33, 20, 1000, tzinfo=timezone.utc)), # just after watershed (1_549_316_052, datetime(2019, 2, 4, 21, 34, 12, 0, tzinfo=timezone.utc)), # nowish in s (1_549_316_052_104, datetime(2019, 2, 4, 21, 34, 12, 104_000, tzinfo=timezone.utc)), # nowish in ms (1_549_316_052_104_324, datetime(2019, 2, 4, 21, 34, 12, 104_324, tzinfo=timezone.utc)), # nowish in μs (1_549_316_052_104_324_096, datetime(2019, 2, 4, 21, 34, 12, 104_324, tzinfo=timezone.utc)), # nowish in ns ("infinity", datetime(9999, 12, 31, 23, 59, 59, 999999)), ("inf", datetime(9999, 12, 31, 23, 59, 59, 999999)), ("inf ", datetime(9999, 12, 31, 23, 59, 59, 999999)), (1e50, datetime(9999, 12, 31, 23, 59, 59, 999999)), (float("inf"), datetime(9999, 12, 31, 23, 59, 59, 999999)), ("-infinity", datetime(1, 1, 1, 0, 0)), ("-inf", datetime(1, 1, 1, 0, 0)), ("nan", ValueError), ], ) def test_datetime_parsing(value: Union[str, bytes, int, float], result: Union[datetime, Type[Exception]]) -> None: if type(result) == type and issubclass(result, Exception): # pyright: ignore[reportUnnecessaryIsInstance] with pytest.raises(result): parse_datetime(value) else: assert parse_datetime(value) == result anthropic-sdk-python-0.120.2/tests/test_utils/test_json.py000066400000000000000000000115571523216435200237000ustar00rootroot00000000000000from __future__ import annotations import datetime from typing import Union import pydantic from anthropic import _compat from anthropic._utils._json import openapi_dumps class TestOpenapiDumps: def test_basic(self) -> None: data = {"key": "value", "number": 42} json_bytes = openapi_dumps(data) assert json_bytes == b'{"key":"value","number":42}' def test_datetime_serialization(self) -> None: dt = datetime.datetime(2023, 1, 1, 12, 0, 0) data = {"datetime": dt} json_bytes = openapi_dumps(data) assert json_bytes == b'{"datetime":"2023-01-01T12:00:00"}' def test_pydantic_model_serialization(self) -> None: class User(pydantic.BaseModel): first_name: str last_name: str age: int model_instance = User(first_name="John", last_name="Kramer", age=83) data = {"model": model_instance} json_bytes = openapi_dumps(data) assert json_bytes == b'{"model":{"first_name":"John","last_name":"Kramer","age":83}}' def test_pydantic_model_with_default_values(self) -> None: class User(pydantic.BaseModel): name: str role: str = "user" active: bool = True score: int = 0 model_instance = User(name="Alice") data = {"model": model_instance} json_bytes = openapi_dumps(data) assert json_bytes == b'{"model":{"name":"Alice"}}' def test_pydantic_model_with_default_values_overridden(self) -> None: class User(pydantic.BaseModel): name: str role: str = "user" active: bool = True model_instance = User(name="Bob", role="admin", active=False) data = {"model": model_instance} json_bytes = openapi_dumps(data) assert json_bytes == b'{"model":{"name":"Bob","role":"admin","active":false}}' def test_pydantic_model_with_alias(self) -> None: class User(pydantic.BaseModel): first_name: str = pydantic.Field(alias="firstName") last_name: str = pydantic.Field(alias="lastName") model_instance = User(firstName="John", lastName="Doe") data = {"model": model_instance} json_bytes = openapi_dumps(data) assert json_bytes == b'{"model":{"firstName":"John","lastName":"Doe"}}' def test_pydantic_model_with_alias_and_default(self) -> None: class User(pydantic.BaseModel): user_name: str = pydantic.Field(alias="userName") user_role: str = pydantic.Field(default="member", alias="userRole") is_active: bool = pydantic.Field(default=True, alias="isActive") model_instance = User(userName="charlie") data = {"model": model_instance} json_bytes = openapi_dumps(data) assert json_bytes == b'{"model":{"userName":"charlie"}}' model_with_overrides = User(userName="diana", userRole="admin", isActive=False) data = {"model": model_with_overrides} json_bytes = openapi_dumps(data) assert json_bytes == b'{"model":{"userName":"diana","userRole":"admin","isActive":false}}' def test_pydantic_model_with_nested_models_and_defaults(self) -> None: class Address(pydantic.BaseModel): street: str city: str = "Unknown" class User(pydantic.BaseModel): name: str address: Address verified: bool = False if _compat.PYDANTIC_V1: # to handle forward references in Pydantic v1 User.update_forward_refs(**locals()) # type: ignore[reportDeprecated] address = Address(street="123 Main St") user = User(name="Diana", address=address) data = {"user": user} json_bytes = openapi_dumps(data) assert json_bytes == b'{"user":{"name":"Diana","address":{"street":"123 Main St"}}}' address_with_city = Address(street="456 Oak Ave", city="Boston") user_verified = User(name="Eve", address=address_with_city, verified=True) data = {"user": user_verified} json_bytes = openapi_dumps(data) assert ( json_bytes == b'{"user":{"name":"Eve","address":{"street":"456 Oak Ave","city":"Boston"},"verified":true}}' ) def test_pydantic_model_with_optional_fields(self) -> None: class User(pydantic.BaseModel): name: str email: Union[str, None] phone: Union[str, None] model_with_none = User(name="Eve", email=None, phone=None) data = {"model": model_with_none} json_bytes = openapi_dumps(data) assert json_bytes == b'{"model":{"name":"Eve","email":null,"phone":null}}' model_with_values = User(name="Frank", email="frank@example.com", phone=None) data = {"model": model_with_values} json_bytes = openapi_dumps(data) assert json_bytes == b'{"model":{"name":"Frank","email":"frank@example.com","phone":null}}' anthropic-sdk-python-0.120.2/tests/test_utils/test_path.py000066400000000000000000000074701523216435200236620ustar00rootroot00000000000000from __future__ import annotations from typing import Any import pytest from anthropic._utils._path import path_template @pytest.mark.parametrize( "template, kwargs, expected", [ ("/v1/{id}", dict(id="abc"), "/v1/abc"), ("/v1/{a}/{b}", dict(a="x", b="y"), "/v1/x/y"), ("/v1/{a}{b}/path/{c}?val={d}#{e}", dict(a="x", b="y", c="z", d="u", e="v"), "/v1/xy/path/z?val=u#v"), ("/{w}/{w}", dict(w="echo"), "/echo/echo"), ("/v1/static", {}, "/v1/static"), ("", {}, ""), ("/v1/?q={n}&count=10", dict(n=42), "/v1/?q=42&count=10"), ("/v1/{v}", dict(v=None), "/v1/null"), ("/v1/{v}", dict(v=True), "/v1/true"), ("/v1/{v}", dict(v=False), "/v1/false"), ("/v1/{v}", dict(v=".hidden"), "/v1/.hidden"), # dot prefix ok ("/v1/{v}", dict(v="file.txt"), "/v1/file.txt"), # dot in middle ok ("/v1/{v}", dict(v="..."), "/v1/..."), # triple dot ok ("/v1/{a}{b}", dict(a=".", b="txt"), "/v1/.txt"), # dot var combining with adjacent to be ok ("/items?q={v}#{f}", dict(v=".", f=".."), "/items?q=.#.."), # dots in query/fragment are fine ( "/v1/{a}?query={b}", dict(a="../../other/endpoint", b="a&bad=true"), "/v1/..%2F..%2Fother%2Fendpoint?query=a%26bad%3Dtrue", ), ("/v1/{val}", dict(val="a/b/c"), "/v1/a%2Fb%2Fc"), ("/v1/{val}", dict(val="a/b/c?query=value"), "/v1/a%2Fb%2Fc%3Fquery=value"), ("/v1/{val}", dict(val="a/b/c?query=value&bad=true"), "/v1/a%2Fb%2Fc%3Fquery=value&bad=true"), ("/v1/{val}", dict(val="%20"), "/v1/%2520"), # escapes escape sequences in input # Query: slash and ? are safe, # is not ("/items?q={v}", dict(v="a/b"), "/items?q=a/b"), ("/items?q={v}", dict(v="a?b"), "/items?q=a?b"), ("/items?q={v}", dict(v="a#b"), "/items?q=a%23b"), ("/items?q={v}", dict(v="a b"), "/items?q=a%20b"), # Fragment: slash and ? are safe ("/docs#{v}", dict(v="a/b"), "/docs#a/b"), ("/docs#{v}", dict(v="a?b"), "/docs#a?b"), # Path: slash, ? and # are all encoded ("/v1/{v}", dict(v="a/b"), "/v1/a%2Fb"), ("/v1/{v}", dict(v="a?b"), "/v1/a%3Fb"), ("/v1/{v}", dict(v="a#b"), "/v1/a%23b"), # same var encoded differently by component ( "/v1/{v}?q={v}#{v}", dict(v="a/b?c#d"), "/v1/a%2Fb%3Fc%23d?q=a/b?c%23d#a/b?c%23d", ), ("/v1/{val}", dict(val="x?admin=true"), "/v1/x%3Fadmin=true"), # query injection ("/v1/{val}", dict(val="x#admin"), "/v1/x%23admin"), # fragment injection ], ) def test_interpolation(template: str, kwargs: dict[str, Any], expected: str) -> None: assert path_template(template, **kwargs) == expected def test_missing_kwarg_raises_key_error() -> None: with pytest.raises(KeyError, match="org_id"): path_template("/v1/{org_id}") @pytest.mark.parametrize( "template, kwargs", [ ("{a}/path", dict(a=".")), ("{a}/path", dict(a="..")), ("/v1/{a}", dict(a=".")), ("/v1/{a}", dict(a="..")), ("/v1/{a}/path", dict(a=".")), ("/v1/{a}/path", dict(a="..")), ("/v1/{a}{b}", dict(a=".", b=".")), # adjacent vars → ".." ("/v1/{a}.", dict(a=".")), # var + static → ".." ("/v1/{a}{b}", dict(a="", b=".")), # empty + dot → "." ("/v1/%2e/{x}", dict(x="ok")), # encoded dot in static text ("/v1/%2e./{x}", dict(x="ok")), # mixed encoded ".." in static ("/v1/.%2E/{x}", dict(x="ok")), # mixed encoded ".." in static ("/v1/{v}?q=1", dict(v="..")), ("/v1/{v}#frag", dict(v="..")), ], ) def test_dot_segment_rejected(template: str, kwargs: dict[str, Any]) -> None: with pytest.raises(ValueError, match="dot-segment"): path_template(template, **kwargs) anthropic-sdk-python-0.120.2/tests/test_utils/test_proxy.py000066400000000000000000000017571523216435200241110ustar00rootroot00000000000000import operator from typing import Any from typing_extensions import override from anthropic._utils import LazyProxy class RecursiveLazyProxy(LazyProxy[Any]): @override def __load__(self) -> Any: return self def __call__(self, *_args: Any, **_kwds: Any) -> Any: raise RuntimeError("This should never be called!") def test_recursive_proxy() -> None: proxy = RecursiveLazyProxy() assert repr(proxy) == "RecursiveLazyProxy" assert str(proxy) == "RecursiveLazyProxy" assert dir(proxy) == [] assert type(proxy).__name__ == "RecursiveLazyProxy" assert type(operator.attrgetter("name.foo.bar.baz")(proxy)).__name__ == "RecursiveLazyProxy" def test_isinstance_does_not_error() -> None: class AlwaysErrorProxy(LazyProxy[Any]): @override def __load__(self) -> Any: raise RuntimeError("Mocking missing dependency") proxy = AlwaysErrorProxy() assert not isinstance(proxy, dict) assert isinstance(proxy, LazyProxy) anthropic-sdk-python-0.120.2/tests/test_utils/test_typing.py000066400000000000000000000046341523216435200242370ustar00rootroot00000000000000from __future__ import annotations from typing import Generic, TypeVar, cast from anthropic._utils import extract_type_var_from_base _T = TypeVar("_T") _T2 = TypeVar("_T2") _T3 = TypeVar("_T3") class BaseGeneric(Generic[_T]): ... class SubclassGeneric(BaseGeneric[_T]): ... class BaseGenericMultipleTypeArgs(Generic[_T, _T2, _T3]): ... class SubclassGenericMultipleTypeArgs(BaseGenericMultipleTypeArgs[_T, _T2, _T3]): ... class SubclassDifferentOrderGenericMultipleTypeArgs(BaseGenericMultipleTypeArgs[_T2, _T, _T3]): ... def test_extract_type_var() -> None: assert ( extract_type_var_from_base( BaseGeneric[int], index=0, generic_bases=cast("tuple[type, ...]", (BaseGeneric,)), ) == int ) def test_extract_type_var_generic_subclass() -> None: assert ( extract_type_var_from_base( SubclassGeneric[int], index=0, generic_bases=cast("tuple[type, ...]", (BaseGeneric,)), ) == int ) def test_extract_type_var_multiple() -> None: typ = BaseGenericMultipleTypeArgs[int, str, None] generic_bases = cast("tuple[type, ...]", (BaseGenericMultipleTypeArgs,)) assert extract_type_var_from_base(typ, index=0, generic_bases=generic_bases) == int assert extract_type_var_from_base(typ, index=1, generic_bases=generic_bases) == str assert extract_type_var_from_base(typ, index=2, generic_bases=generic_bases) == type(None) def test_extract_type_var_generic_subclass_multiple() -> None: typ = SubclassGenericMultipleTypeArgs[int, str, None] generic_bases = cast("tuple[type, ...]", (BaseGenericMultipleTypeArgs,)) assert extract_type_var_from_base(typ, index=0, generic_bases=generic_bases) == int assert extract_type_var_from_base(typ, index=1, generic_bases=generic_bases) == str assert extract_type_var_from_base(typ, index=2, generic_bases=generic_bases) == type(None) def test_extract_type_var_generic_subclass_different_ordering_multiple() -> None: typ = SubclassDifferentOrderGenericMultipleTypeArgs[int, str, None] generic_bases = cast("tuple[type, ...]", (BaseGenericMultipleTypeArgs,)) assert extract_type_var_from_base(typ, index=0, generic_bases=generic_bases) == int assert extract_type_var_from_base(typ, index=1, generic_bases=generic_bases) == str assert extract_type_var_from_base(typ, index=2, generic_bases=generic_bases) == type(None) anthropic-sdk-python-0.120.2/tests/utils.py000066400000000000000000000115731523216435200206270ustar00rootroot00000000000000from __future__ import annotations import os import inspect import traceback import contextlib from typing import Any, TypeVar, Iterator, Sequence, cast from datetime import date, datetime from typing_extensions import Literal, get_args, get_origin, assert_type from anthropic._types import Omit, NoneType from anthropic._utils import ( is_dict, is_list, is_list_type, is_union_type, extract_type_arg, is_sequence_type, is_annotated_type, is_type_alias_type, ) from anthropic._compat import PYDANTIC_V1, field_outer_type, get_model_fields from anthropic._models import BaseModel BaseModelT = TypeVar("BaseModelT", bound=BaseModel) def assert_matches_model(model: type[BaseModelT], value: BaseModelT, *, path: list[str]) -> bool: for name, field in get_model_fields(model).items(): field_value = getattr(value, name) if PYDANTIC_V1: # in v1 nullability was structured differently # https://docs.pydantic.dev/2.0/migration/#required-optional-and-nullable-fields allow_none = getattr(field, "allow_none", False) else: allow_none = False assert_matches_type( field_outer_type(field), field_value, path=[*path, name], allow_none=allow_none, ) return True # Note: the `path` argument is only used to improve error messages when `--showlocals` is used def assert_matches_type( type_: Any, value: object, *, path: list[str], allow_none: bool = False, ) -> None: if is_type_alias_type(type_): type_ = type_.__value__ # unwrap `Annotated[T, ...]` -> `T` if is_annotated_type(type_): type_ = extract_type_arg(type_, 0) if allow_none and value is None: return if type_ is None or type_ is NoneType: assert value is None return origin = get_origin(type_) or type_ if is_list_type(type_): return _assert_list_type(type_, value) if is_sequence_type(type_): assert isinstance(value, Sequence) inner_type = get_args(type_)[0] for entry in value: # type: ignore assert_type(inner_type, entry) # type: ignore return if origin == str: assert isinstance(value, str) elif origin == int: assert isinstance(value, int) elif origin == bool: assert isinstance(value, bool) elif origin == float: assert isinstance(value, float) elif origin == bytes: assert isinstance(value, bytes) elif origin == datetime: assert isinstance(value, datetime) elif origin == date: assert isinstance(value, date) elif origin == object: # nothing to do here, the expected type is unknown pass elif origin == Literal: assert value in get_args(type_) elif origin == dict: assert is_dict(value) args = get_args(type_) key_type = args[0] items_type = args[1] for key, item in value.items(): assert_matches_type(key_type, key, path=[*path, ""]) assert_matches_type(items_type, item, path=[*path, ""]) elif is_union_type(type_): variants = get_args(type_) try: none_index = variants.index(type(None)) except ValueError: pass else: # special case Optional[T] for better error messages if len(variants) == 2: if value is None: # valid return return assert_matches_type(type_=variants[not none_index], value=value, path=path) for i, variant in enumerate(variants): try: assert_matches_type(variant, value, path=[*path, f"variant {i}"]) return except AssertionError: traceback.print_exc() continue raise AssertionError("Did not match any variants") elif issubclass(origin, BaseModel): assert isinstance(value, type_) assert assert_matches_model(type_, cast(Any, value), path=path) elif inspect.isclass(origin) and origin.__name__ == "HttpxBinaryResponseContent": assert value.__class__.__name__ == "HttpxBinaryResponseContent" else: assert None, f"Unhandled field type: {type_}" def _assert_list_type(type_: type[object], value: object) -> None: assert is_list(value) inner_type = get_args(type_)[0] for entry in value: assert_type(inner_type, entry) # type: ignore @contextlib.contextmanager def update_env(**new_env: str | Omit) -> Iterator[None]: old = os.environ.copy() try: for name, value in new_env.items(): if isinstance(value, Omit): os.environ.pop(name, None) else: os.environ[name] = value yield None finally: os.environ.clear() os.environ.update(old) anthropic-sdk-python-0.120.2/tools.md000066400000000000000000000062241523216435200174320ustar00rootroot00000000000000# Tools helpers To define a tool, you can use the `@beta_tool` decorator on any python function like so: ```python from anthropic import beta_tool @beta_tool def sum(left: int, right: int) -> str: """Adds two integers together. Args: left (int): The first integer to add. right (int): The second integer to add. Returns: int: The sum of left and right integers. """ return str(left + right) ``` > [!TIP] > If you're using the async client, replace `@beta_tool` with `@beta_async_tool` and define the function with `async def`. The `@beta_tool` decorator will inspect the function arguments and the docstring to extract a json schema representation of the given function, in this case it'll be turned into: ```json { "name": "sum", "description": "Adds two integers together.", "input_schema": { "additionalProperties": false, "properties": { "left": { "description": "The first integer to add.", "title": "Left", "type": "integer" }, "right": { "description": "The second integer to add.", "title": "Right", "type": "integer" } }, "required": ["left", "right"], "type": "object" } } ``` If you want to implement calling the tool yourself, you can then pass the to the API like so: ```python message = client.beta.messages.create( tools=[get_weather.to_dict()], # ... max_tokens=1024, model="claude-sonnet-4-5-20250929", messages=[{"role": "user", "content": "What is 2 + 2?"}], ) ``` or you can use our [tool runner](#tool-runner)! ## Tool runner We provide a `client.beta.messages.tool_runner()` method that can automatically call tools defined with `@beta_tool()`. This method returns a `BetaToolRunner` class that is an iterator where each iteration yields a new `BetaMessage` instance from an API call, iteration will stop when there no tool call content blocks. ```py runner = client.beta.messages.tool_runner( max_tokens=1024, model="claude-sonnet-4-5-20250929", tools=[sum], messages=[{"role": "user", "content": "What is 9 + 10?"}], ) for message in runner: rich.print(message) ``` ## ToolError To report an error from a tool back to the model, raise a `ToolError`. Unlike a plain exception, `ToolError` accepts content blocks, allowing you to include images or other structured content in the error response: ```py from anthropic import beta_tool from anthropic.lib.tools import ToolError @beta_tool def take_screenshot(url: str) -> str: """Take a screenshot of a URL.""" if not is_valid_url(url): raise ToolError(f"Invalid URL: {url}") result = capture(url) if result.error: # Include the error screenshot so the model can see what went wrong raise ToolError([ {"type": "text", "text": f"Failed to load page: {result.error}"}, {"type": "image", "source": {"type": "base64", "data": result.screenshot, "media_type": "image/png"}}, ]) return result.data ``` If a plain exception is raised, its `repr()` will be sent to the model as a text error and logged. `ToolError` is not logged since it represents an intentional error response. anthropic-sdk-python-0.120.2/uv.lock000066400000000000000000024774461523216435200173000ustar00rootroot00000000000000version = 1 revision = 3 requires-python = ">=3.9" resolution-markers = [ "python_full_version >= '3.14' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.14' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and python_full_version < '3.14' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and python_full_version < '3.14' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version < '3.10' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version < '3.10' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra != 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", "python_full_version < '3.10' and extra != 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", "python_full_version < '3.10' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", "python_full_version < '3.10' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", ] conflicts = [[ { package = "anthropic", group = "pydantic-v1" }, { package = "anthropic", group = "pydantic-v2" }, ], [ { package = "anthropic", extra = "mcp" }, { package = "anthropic", group = "pydantic-v1" }, ]] [[package]] name = "aiohappyeyeballs" version = "2.6.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, ] [[package]] name = "aiohttp" version = "3.13.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohappyeyeballs" }, { name = "aiosignal" }, { name = "async-timeout", marker = "python_full_version < '3.11' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "attrs" }, { name = "frozenlist" }, { name = "multidict" }, { name = "propcache" }, { name = "yarl", version = "1.22.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "yarl", version = "1.23.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/42/32cf8e7704ceb4481406eb87161349abb46a57fee3f008ba9cb610968646/aiohttp-3.13.3.tar.gz", hash = "sha256:a949eee43d3782f2daae4f4a2819b2cb9b0c5d3b7f7a927067cc84dafdbb9f88", size = 7844556, upload-time = "2026-01-03T17:33:05.204Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/36/d6/5aec9313ee6ea9c7cde8b891b69f4ff4001416867104580670a31daeba5b/aiohttp-3.13.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5a372fd5afd301b3a89582817fdcdb6c34124787c70dbcc616f259013e7eef7", size = 738950, upload-time = "2026-01-03T17:29:13.002Z" }, { url = "https://files.pythonhosted.org/packages/68/03/8fa90a7e6d11ff20a18837a8e2b5dd23db01aabc475aa9271c8ad33299f5/aiohttp-3.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:147e422fd1223005c22b4fe080f5d93ced44460f5f9c105406b753612b587821", size = 496099, upload-time = "2026-01-03T17:29:15.268Z" }, { url = "https://files.pythonhosted.org/packages/d2/23/b81f744d402510a8366b74eb420fc0cc1170d0c43daca12d10814df85f10/aiohttp-3.13.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:859bd3f2156e81dd01432f5849fc73e2243d4a487c4fd26609b1299534ee1845", size = 491072, upload-time = "2026-01-03T17:29:16.922Z" }, { url = "https://files.pythonhosted.org/packages/d5/e1/56d1d1c0dd334cd203dd97706ce004c1aa24b34a813b0b8daf3383039706/aiohttp-3.13.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dca68018bf48c251ba17c72ed479f4dafe9dbd5a73707ad8d28a38d11f3d42af", size = 1671588, upload-time = "2026-01-03T17:29:18.539Z" }, { url = "https://files.pythonhosted.org/packages/5f/34/8d7f962604f4bc2b4e39eb1220dac7d4e4cba91fb9ba0474b4ecd67db165/aiohttp-3.13.3-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fee0c6bc7db1de362252affec009707a17478a00ec69f797d23ca256e36d5940", size = 1640334, upload-time = "2026-01-03T17:29:21.028Z" }, { url = "https://files.pythonhosted.org/packages/94/1d/fcccf2c668d87337ddeef9881537baee13c58d8f01f12ba8a24215f2b804/aiohttp-3.13.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c048058117fd649334d81b4b526e94bde3ccaddb20463a815ced6ecbb7d11160", size = 1722656, upload-time = "2026-01-03T17:29:22.531Z" }, { url = "https://files.pythonhosted.org/packages/aa/98/c6f3b081c4c606bc1e5f2ec102e87d6411c73a9ef3616fea6f2d5c98c062/aiohttp-3.13.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:215a685b6fbbfcf71dfe96e3eba7a6f58f10da1dfdf4889c7dd856abe430dca7", size = 1817625, upload-time = "2026-01-03T17:29:24.276Z" }, { url = "https://files.pythonhosted.org/packages/2c/c0/cfcc3d2e11b477f86e1af2863f3858c8850d751ce8dc39c4058a072c9e54/aiohttp-3.13.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2c184bb1fe2cbd2cefba613e9db29a5ab559323f994b6737e370d3da0ac455", size = 1672604, upload-time = "2026-01-03T17:29:26.099Z" }, { url = "https://files.pythonhosted.org/packages/1e/77/6b4ffcbcac4c6a5d041343a756f34a6dd26174ae07f977a64fe028dda5b0/aiohttp-3.13.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:75ca857eba4e20ce9f546cd59c7007b33906a4cd48f2ff6ccf1ccfc3b646f279", size = 1554370, upload-time = "2026-01-03T17:29:28.121Z" }, { url = "https://files.pythonhosted.org/packages/f2/f0/e3ddfa93f17d689dbe014ba048f18e0c9f9b456033b70e94349a2e9048be/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81e97251d9298386c2b7dbeb490d3d1badbdc69107fb8c9299dd04eb39bddc0e", size = 1642023, upload-time = "2026-01-03T17:29:30.002Z" }, { url = "https://files.pythonhosted.org/packages/eb/45/c14019c9ec60a8e243d06d601b33dcc4fd92379424bde3021725859d7f99/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c0e2d366af265797506f0283487223146af57815b388623f0357ef7eac9b209d", size = 1649680, upload-time = "2026-01-03T17:29:31.782Z" }, { url = "https://files.pythonhosted.org/packages/9c/fd/09c9451dae5aa5c5ed756df95ff9ef549d45d4be663bafd1e4954fd836f0/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4e239d501f73d6db1522599e14b9b321a7e3b1de66ce33d53a765d975e9f4808", size = 1692407, upload-time = "2026-01-03T17:29:33.392Z" }, { url = "https://files.pythonhosted.org/packages/a6/81/938bc2ec33c10efd6637ccb3d22f9f3160d08e8f3aa2587a2c2d5ab578eb/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:0db318f7a6f065d84cb1e02662c526294450b314a02bd9e2a8e67f0d8564ce40", size = 1543047, upload-time = "2026-01-03T17:29:34.855Z" }, { url = "https://files.pythonhosted.org/packages/f7/23/80488ee21c8d567c83045e412e1d9b7077d27171591a4eb7822586e8c06a/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:bfc1cc2fe31a6026a8a88e4ecfb98d7f6b1fec150cfd708adbfd1d2f42257c29", size = 1715264, upload-time = "2026-01-03T17:29:36.389Z" }, { url = "https://files.pythonhosted.org/packages/e2/83/259a8da6683182768200b368120ab3deff5370bed93880fb9a3a86299f34/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af71fff7bac6bb7508956696dce8f6eec2bbb045eceb40343944b1ae62b5ef11", size = 1657275, upload-time = "2026-01-03T17:29:38.162Z" }, { url = "https://files.pythonhosted.org/packages/3f/4f/2c41f800a0b560785c10fb316216ac058c105f9be50bdc6a285de88db625/aiohttp-3.13.3-cp310-cp310-win32.whl", hash = "sha256:37da61e244d1749798c151421602884db5270faf479cf0ef03af0ff68954c9dd", size = 434053, upload-time = "2026-01-03T17:29:40.074Z" }, { url = "https://files.pythonhosted.org/packages/80/df/29cd63c7ecfdb65ccc12f7d808cac4fa2a19544660c06c61a4a48462de0c/aiohttp-3.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:7e63f210bc1b57ef699035f2b4b6d9ce096b5914414a49b0997c839b2bd2223c", size = 456687, upload-time = "2026-01-03T17:29:41.819Z" }, { url = "https://files.pythonhosted.org/packages/f1/4c/a164164834f03924d9a29dc3acd9e7ee58f95857e0b467f6d04298594ebb/aiohttp-3.13.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5b6073099fb654e0a068ae678b10feff95c5cae95bbfcbfa7af669d361a8aa6b", size = 746051, upload-time = "2026-01-03T17:29:43.287Z" }, { url = "https://files.pythonhosted.org/packages/82/71/d5c31390d18d4f58115037c432b7e0348c60f6f53b727cad33172144a112/aiohttp-3.13.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cb93e166e6c28716c8c6aeb5f99dfb6d5ccf482d29fe9bf9a794110e6d0ab64", size = 499234, upload-time = "2026-01-03T17:29:44.822Z" }, { url = "https://files.pythonhosted.org/packages/0e/c9/741f8ac91e14b1d2e7100690425a5b2b919a87a5075406582991fb7de920/aiohttp-3.13.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:28e027cf2f6b641693a09f631759b4d9ce9165099d2b5d92af9bd4e197690eea", size = 494979, upload-time = "2026-01-03T17:29:46.405Z" }, { url = "https://files.pythonhosted.org/packages/75/b5/31d4d2e802dfd59f74ed47eba48869c1c21552c586d5e81a9d0d5c2ad640/aiohttp-3.13.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3b61b7169ababd7802f9568ed96142616a9118dd2be0d1866e920e77ec8fa92a", size = 1748297, upload-time = "2026-01-03T17:29:48.083Z" }, { url = "https://files.pythonhosted.org/packages/1a/3e/eefad0ad42959f226bb79664826883f2687d602a9ae2941a18e0484a74d3/aiohttp-3.13.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:80dd4c21b0f6237676449c6baaa1039abae86b91636b6c91a7f8e61c87f89540", size = 1707172, upload-time = "2026-01-03T17:29:49.648Z" }, { url = "https://files.pythonhosted.org/packages/c5/3a/54a64299fac2891c346cdcf2aa6803f994a2e4beeaf2e5a09dcc54acc842/aiohttp-3.13.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:65d2ccb7eabee90ce0503c17716fc77226be026dcc3e65cce859a30db715025b", size = 1805405, upload-time = "2026-01-03T17:29:51.244Z" }, { url = "https://files.pythonhosted.org/packages/6c/70/ddc1b7169cf64075e864f64595a14b147a895a868394a48f6a8031979038/aiohttp-3.13.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5b179331a481cb5529fca8b432d8d3c7001cb217513c94cd72d668d1248688a3", size = 1899449, upload-time = "2026-01-03T17:29:53.938Z" }, { url = "https://files.pythonhosted.org/packages/a1/7e/6815aab7d3a56610891c76ef79095677b8b5be6646aaf00f69b221765021/aiohttp-3.13.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d4c940f02f49483b18b079d1c27ab948721852b281f8b015c058100e9421dd1", size = 1748444, upload-time = "2026-01-03T17:29:55.484Z" }, { url = "https://files.pythonhosted.org/packages/6b/f2/073b145c4100da5511f457dc0f7558e99b2987cf72600d42b559db856fbc/aiohttp-3.13.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f9444f105664c4ce47a2a7171a2418bce5b7bae45fb610f4e2c36045d85911d3", size = 1606038, upload-time = "2026-01-03T17:29:57.179Z" }, { url = "https://files.pythonhosted.org/packages/0a/c1/778d011920cae03ae01424ec202c513dc69243cf2db303965615b81deeea/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:694976222c711d1d00ba131904beb60534f93966562f64440d0c9d41b8cdb440", size = 1724156, upload-time = "2026-01-03T17:29:58.914Z" }, { url = "https://files.pythonhosted.org/packages/0e/cb/3419eabf4ec1e9ec6f242c32b689248365a1cf621891f6f0386632525494/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f33ed1a2bf1997a36661874b017f5c4b760f41266341af36febaf271d179f6d7", size = 1722340, upload-time = "2026-01-03T17:30:01.962Z" }, { url = "https://files.pythonhosted.org/packages/7a/e5/76cf77bdbc435bf233c1f114edad39ed4177ccbfab7c329482b179cff4f4/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e636b3c5f61da31a92bf0d91da83e58fdfa96f178ba682f11d24f31944cdd28c", size = 1783041, upload-time = "2026-01-03T17:30:03.609Z" }, { url = "https://files.pythonhosted.org/packages/9d/d4/dd1ca234c794fd29c057ce8c0566b8ef7fd6a51069de5f06fa84b9a1971c/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5d2d94f1f5fcbe40838ac51a6ab5704a6f9ea42e72ceda48de5e6b898521da51", size = 1596024, upload-time = "2026-01-03T17:30:05.132Z" }, { url = "https://files.pythonhosted.org/packages/55/58/4345b5f26661a6180afa686c473620c30a66afdf120ed3dd545bbc809e85/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2be0e9ccf23e8a94f6f0650ce06042cefc6ac703d0d7ab6c7a917289f2539ad4", size = 1804590, upload-time = "2026-01-03T17:30:07.135Z" }, { url = "https://files.pythonhosted.org/packages/7b/06/05950619af6c2df7e0a431d889ba2813c9f0129cec76f663e547a5ad56f2/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9af5e68ee47d6534d36791bbe9b646d2a7c7deb6fc24d7943628edfbb3581f29", size = 1740355, upload-time = "2026-01-03T17:30:09.083Z" }, { url = "https://files.pythonhosted.org/packages/3e/80/958f16de79ba0422d7c1e284b2abd0c84bc03394fbe631d0a39ffa10e1eb/aiohttp-3.13.3-cp311-cp311-win32.whl", hash = "sha256:a2212ad43c0833a873d0fb3c63fa1bacedd4cf6af2fee62bf4b739ceec3ab239", size = 433701, upload-time = "2026-01-03T17:30:10.869Z" }, { url = "https://files.pythonhosted.org/packages/dc/f2/27cdf04c9851712d6c1b99df6821a6623c3c9e55956d4b1e318c337b5a48/aiohttp-3.13.3-cp311-cp311-win_amd64.whl", hash = "sha256:642f752c3eb117b105acbd87e2c143de710987e09860d674e068c4c2c441034f", size = 457678, upload-time = "2026-01-03T17:30:12.719Z" }, { url = "https://files.pythonhosted.org/packages/a0/be/4fc11f202955a69e0db803a12a062b8379c970c7c84f4882b6da17337cc1/aiohttp-3.13.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b903a4dfee7d347e2d87697d0713be59e0b87925be030c9178c5faa58ea58d5c", size = 739732, upload-time = "2026-01-03T17:30:14.23Z" }, { url = "https://files.pythonhosted.org/packages/97/2c/621d5b851f94fa0bb7430d6089b3aa970a9d9b75196bc93bb624b0db237a/aiohttp-3.13.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a45530014d7a1e09f4a55f4f43097ba0fd155089372e105e4bff4ca76cb1b168", size = 494293, upload-time = "2026-01-03T17:30:15.96Z" }, { url = "https://files.pythonhosted.org/packages/5d/43/4be01406b78e1be8320bb8316dc9c42dbab553d281c40364e0f862d5661c/aiohttp-3.13.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:27234ef6d85c914f9efeb77ff616dbf4ad2380be0cda40b4db086ffc7ddd1b7d", size = 493533, upload-time = "2026-01-03T17:30:17.431Z" }, { url = "https://files.pythonhosted.org/packages/8d/a8/5a35dc56a06a2c90d4742cbf35294396907027f80eea696637945a106f25/aiohttp-3.13.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d32764c6c9aafb7fb55366a224756387cd50bfa720f32b88e0e6fa45b27dcf29", size = 1737839, upload-time = "2026-01-03T17:30:19.422Z" }, { url = "https://files.pythonhosted.org/packages/bf/62/4b9eeb331da56530bf2e198a297e5303e1c1ebdceeb00fe9b568a65c5a0c/aiohttp-3.13.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b1a6102b4d3ebc07dad44fbf07b45bb600300f15b552ddf1851b5390202ea2e3", size = 1703932, upload-time = "2026-01-03T17:30:21.756Z" }, { url = "https://files.pythonhosted.org/packages/7c/f6/af16887b5d419e6a367095994c0b1332d154f647e7dc2bd50e61876e8e3d/aiohttp-3.13.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c014c7ea7fb775dd015b2d3137378b7be0249a448a1612268b5a90c2d81de04d", size = 1771906, upload-time = "2026-01-03T17:30:23.932Z" }, { url = "https://files.pythonhosted.org/packages/ce/83/397c634b1bcc24292fa1e0c7822800f9f6569e32934bdeef09dae7992dfb/aiohttp-3.13.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2b8d8ddba8f95ba17582226f80e2de99c7a7948e66490ef8d947e272a93e9463", size = 1871020, upload-time = "2026-01-03T17:30:26Z" }, { url = "https://files.pythonhosted.org/packages/86/f6/a62cbbf13f0ac80a70f71b1672feba90fdb21fd7abd8dbf25c0105fb6fa3/aiohttp-3.13.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ae8dd55c8e6c4257eae3a20fd2c8f41edaea5992ed67156642493b8daf3cecc", size = 1755181, upload-time = "2026-01-03T17:30:27.554Z" }, { url = "https://files.pythonhosted.org/packages/0a/87/20a35ad487efdd3fba93d5843efdfaa62d2f1479eaafa7453398a44faf13/aiohttp-3.13.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:01ad2529d4b5035578f5081606a465f3b814c542882804e2e8cda61adf5c71bf", size = 1561794, upload-time = "2026-01-03T17:30:29.254Z" }, { url = "https://files.pythonhosted.org/packages/de/95/8fd69a66682012f6716e1bc09ef8a1a2a91922c5725cb904689f112309c4/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bb4f7475e359992b580559e008c598091c45b5088f28614e855e42d39c2f1033", size = 1697900, upload-time = "2026-01-03T17:30:31.033Z" }, { url = "https://files.pythonhosted.org/packages/e5/66/7b94b3b5ba70e955ff597672dad1691333080e37f50280178967aff68657/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c19b90316ad3b24c69cd78d5c9b4f3aa4497643685901185b65166293d36a00f", size = 1728239, upload-time = "2026-01-03T17:30:32.703Z" }, { url = "https://files.pythonhosted.org/packages/47/71/6f72f77f9f7d74719692ab65a2a0252584bf8d5f301e2ecb4c0da734530a/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:96d604498a7c782cb15a51c406acaea70d8c027ee6b90c569baa6e7b93073679", size = 1740527, upload-time = "2026-01-03T17:30:34.695Z" }, { url = "https://files.pythonhosted.org/packages/fa/b4/75ec16cbbd5c01bdaf4a05b19e103e78d7ce1ef7c80867eb0ace42ff4488/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:084911a532763e9d3dd95adf78a78f4096cd5f58cdc18e6fdbc1b58417a45423", size = 1554489, upload-time = "2026-01-03T17:30:36.864Z" }, { url = "https://files.pythonhosted.org/packages/52/8f/bc518c0eea29f8406dcf7ed1f96c9b48e3bc3995a96159b3fc11f9e08321/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7a4a94eb787e606d0a09404b9c38c113d3b099d508021faa615d70a0131907ce", size = 1767852, upload-time = "2026-01-03T17:30:39.433Z" }, { url = "https://files.pythonhosted.org/packages/9d/f2/a07a75173124f31f11ea6f863dc44e6f09afe2bca45dd4e64979490deab1/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:87797e645d9d8e222e04160ee32aa06bc5c163e8499f24db719e7852ec23093a", size = 1722379, upload-time = "2026-01-03T17:30:41.081Z" }, { url = "https://files.pythonhosted.org/packages/3c/4a/1a3fee7c21350cac78e5c5cef711bac1b94feca07399f3d406972e2d8fcd/aiohttp-3.13.3-cp312-cp312-win32.whl", hash = "sha256:b04be762396457bef43f3597c991e192ee7da460a4953d7e647ee4b1c28e7046", size = 428253, upload-time = "2026-01-03T17:30:42.644Z" }, { url = "https://files.pythonhosted.org/packages/d9/b7/76175c7cb4eb73d91ad63c34e29fc4f77c9386bba4a65b53ba8e05ee3c39/aiohttp-3.13.3-cp312-cp312-win_amd64.whl", hash = "sha256:e3531d63d3bdfa7e3ac5e9b27b2dd7ec9df3206a98e0b3445fa906f233264c57", size = 455407, upload-time = "2026-01-03T17:30:44.195Z" }, { url = "https://files.pythonhosted.org/packages/97/8a/12ca489246ca1faaf5432844adbfce7ff2cc4997733e0af120869345643a/aiohttp-3.13.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5dff64413671b0d3e7d5918ea490bdccb97a4ad29b3f311ed423200b2203e01c", size = 734190, upload-time = "2026-01-03T17:30:45.832Z" }, { url = "https://files.pythonhosted.org/packages/32/08/de43984c74ed1fca5c014808963cc83cb00d7bb06af228f132d33862ca76/aiohttp-3.13.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:87b9aab6d6ed88235aa2970294f496ff1a1f9adcd724d800e9b952395a80ffd9", size = 491783, upload-time = "2026-01-03T17:30:47.466Z" }, { url = "https://files.pythonhosted.org/packages/17/f8/8dd2cf6112a5a76f81f81a5130c57ca829d101ad583ce57f889179accdda/aiohttp-3.13.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:425c126c0dc43861e22cb1c14ba4c8e45d09516d0a3ae0a3f7494b79f5f233a3", size = 490704, upload-time = "2026-01-03T17:30:49.373Z" }, { url = "https://files.pythonhosted.org/packages/6d/40/a46b03ca03936f832bc7eaa47cfbb1ad012ba1be4790122ee4f4f8cba074/aiohttp-3.13.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f9120f7093c2a32d9647abcaf21e6ad275b4fbec5b55969f978b1a97c7c86bf", size = 1720652, upload-time = "2026-01-03T17:30:50.974Z" }, { url = "https://files.pythonhosted.org/packages/f7/7e/917fe18e3607af92657e4285498f500dca797ff8c918bd7d90b05abf6c2a/aiohttp-3.13.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:697753042d57f4bf7122cab985bf15d0cef23c770864580f5af4f52023a56bd6", size = 1692014, upload-time = "2026-01-03T17:30:52.729Z" }, { url = "https://files.pythonhosted.org/packages/71/b6/cefa4cbc00d315d68973b671cf105b21a609c12b82d52e5d0c9ae61d2a09/aiohttp-3.13.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6de499a1a44e7de70735d0b39f67c8f25eb3d91eb3103be99ca0fa882cdd987d", size = 1759777, upload-time = "2026-01-03T17:30:54.537Z" }, { url = "https://files.pythonhosted.org/packages/fb/e3/e06ee07b45e59e6d81498b591fc589629be1553abb2a82ce33efe2a7b068/aiohttp-3.13.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:37239e9f9a7ea9ac5bf6b92b0260b01f8a22281996da609206a84df860bc1261", size = 1861276, upload-time = "2026-01-03T17:30:56.512Z" }, { url = "https://files.pythonhosted.org/packages/7c/24/75d274228acf35ceeb2850b8ce04de9dd7355ff7a0b49d607ee60c29c518/aiohttp-3.13.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f76c1e3fe7d7c8afad7ed193f89a292e1999608170dcc9751a7462a87dfd5bc0", size = 1743131, upload-time = "2026-01-03T17:30:58.256Z" }, { url = "https://files.pythonhosted.org/packages/04/98/3d21dde21889b17ca2eea54fdcff21b27b93f45b7bb94ca029c31ab59dc3/aiohttp-3.13.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fc290605db2a917f6e81b0e1e0796469871f5af381ce15c604a3c5c7e51cb730", size = 1556863, upload-time = "2026-01-03T17:31:00.445Z" }, { url = "https://files.pythonhosted.org/packages/9e/84/da0c3ab1192eaf64782b03971ab4055b475d0db07b17eff925e8c93b3aa5/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4021b51936308aeea0367b8f006dc999ca02bc118a0cc78c303f50a2ff6afb91", size = 1682793, upload-time = "2026-01-03T17:31:03.024Z" }, { url = "https://files.pythonhosted.org/packages/ff/0f/5802ada182f575afa02cbd0ec5180d7e13a402afb7c2c03a9aa5e5d49060/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:49a03727c1bba9a97d3e93c9f93ca03a57300f484b6e935463099841261195d3", size = 1716676, upload-time = "2026-01-03T17:31:04.842Z" }, { url = "https://files.pythonhosted.org/packages/3f/8c/714d53bd8b5a4560667f7bbbb06b20c2382f9c7847d198370ec6526af39c/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3d9908a48eb7416dc1f4524e69f1d32e5d90e3981e4e37eb0aa1cd18f9cfa2a4", size = 1733217, upload-time = "2026-01-03T17:31:06.868Z" }, { url = "https://files.pythonhosted.org/packages/7d/79/e2176f46d2e963facea939f5be2d26368ce543622be6f00a12844d3c991f/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2712039939ec963c237286113c68dbad80a82a4281543f3abf766d9d73228998", size = 1552303, upload-time = "2026-01-03T17:31:08.958Z" }, { url = "https://files.pythonhosted.org/packages/ab/6a/28ed4dea1759916090587d1fe57087b03e6c784a642b85ef48217b0277ae/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:7bfdc049127717581866fa4708791220970ce291c23e28ccf3922c700740fdc0", size = 1763673, upload-time = "2026-01-03T17:31:10.676Z" }, { url = "https://files.pythonhosted.org/packages/e8/35/4a3daeb8b9fab49240d21c04d50732313295e4bd813a465d840236dd0ce1/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8057c98e0c8472d8846b9c79f56766bcc57e3e8ac7bfd510482332366c56c591", size = 1721120, upload-time = "2026-01-03T17:31:12.575Z" }, { url = "https://files.pythonhosted.org/packages/bc/9f/d643bb3c5fb99547323e635e251c609fbbc660d983144cfebec529e09264/aiohttp-3.13.3-cp313-cp313-win32.whl", hash = "sha256:1449ceddcdbcf2e0446957863af03ebaaa03f94c090f945411b61269e2cb5daf", size = 427383, upload-time = "2026-01-03T17:31:14.382Z" }, { url = "https://files.pythonhosted.org/packages/4e/f1/ab0395f8a79933577cdd996dd2f9aa6014af9535f65dddcf88204682fe62/aiohttp-3.13.3-cp313-cp313-win_amd64.whl", hash = "sha256:693781c45a4033d31d4187d2436f5ac701e7bbfe5df40d917736108c1cc7436e", size = 453899, upload-time = "2026-01-03T17:31:15.958Z" }, { url = "https://files.pythonhosted.org/packages/99/36/5b6514a9f5d66f4e2597e40dea2e3db271e023eb7a5d22defe96ba560996/aiohttp-3.13.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:ea37047c6b367fd4bd632bff8077449b8fa034b69e812a18e0132a00fae6e808", size = 737238, upload-time = "2026-01-03T17:31:17.909Z" }, { url = "https://files.pythonhosted.org/packages/f7/49/459327f0d5bcd8c6c9ca69e60fdeebc3622861e696490d8674a6d0cb90a6/aiohttp-3.13.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6fc0e2337d1a4c3e6acafda6a78a39d4c14caea625124817420abceed36e2415", size = 492292, upload-time = "2026-01-03T17:31:19.919Z" }, { url = "https://files.pythonhosted.org/packages/e8/0b/b97660c5fd05d3495b4eb27f2d0ef18dc1dc4eff7511a9bf371397ff0264/aiohttp-3.13.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c685f2d80bb67ca8c3837823ad76196b3694b0159d232206d1e461d3d434666f", size = 493021, upload-time = "2026-01-03T17:31:21.636Z" }, { url = "https://files.pythonhosted.org/packages/54/d4/438efabdf74e30aeceb890c3290bbaa449780583b1270b00661126b8aae4/aiohttp-3.13.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e377758516d262bde50c2584fc6c578af272559c409eecbdd2bae1601184d6", size = 1717263, upload-time = "2026-01-03T17:31:23.296Z" }, { url = "https://files.pythonhosted.org/packages/71/f2/7bddc7fd612367d1459c5bcf598a9e8f7092d6580d98de0e057eb42697ad/aiohttp-3.13.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:34749271508078b261c4abb1767d42b8d0c0cc9449c73a4df494777dc55f0687", size = 1669107, upload-time = "2026-01-03T17:31:25.334Z" }, { url = "https://files.pythonhosted.org/packages/00/5a/1aeaecca40e22560f97610a329e0e5efef5e0b5afdf9f857f0d93839ab2e/aiohttp-3.13.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:82611aeec80eb144416956ec85b6ca45a64d76429c1ed46ae1b5f86c6e0c9a26", size = 1760196, upload-time = "2026-01-03T17:31:27.394Z" }, { url = "https://files.pythonhosted.org/packages/f8/f8/0ff6992bea7bd560fc510ea1c815f87eedd745fe035589c71ce05612a19a/aiohttp-3.13.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2fff83cfc93f18f215896e3a190e8e5cb413ce01553901aca925176e7568963a", size = 1843591, upload-time = "2026-01-03T17:31:29.238Z" }, { url = "https://files.pythonhosted.org/packages/e3/d1/e30e537a15f53485b61f5be525f2157da719819e8377298502aebac45536/aiohttp-3.13.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bbe7d4cecacb439e2e2a8a1a7b935c25b812af7a5fd26503a66dadf428e79ec1", size = 1720277, upload-time = "2026-01-03T17:31:31.053Z" }, { url = "https://files.pythonhosted.org/packages/84/45/23f4c451d8192f553d38d838831ebbc156907ea6e05557f39563101b7717/aiohttp-3.13.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b928f30fe49574253644b1ca44b1b8adbd903aa0da4b9054a6c20fc7f4092a25", size = 1548575, upload-time = "2026-01-03T17:31:32.87Z" }, { url = "https://files.pythonhosted.org/packages/6a/ed/0a42b127a43712eda7807e7892c083eadfaf8429ca8fb619662a530a3aab/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7b5e8fe4de30df199155baaf64f2fcd604f4c678ed20910db8e2c66dc4b11603", size = 1679455, upload-time = "2026-01-03T17:31:34.76Z" }, { url = "https://files.pythonhosted.org/packages/2e/b5/c05f0c2b4b4fe2c9d55e73b6d3ed4fd6c9dc2684b1d81cbdf77e7fad9adb/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:8542f41a62bcc58fc7f11cf7c90e0ec324ce44950003feb70640fc2a9092c32a", size = 1687417, upload-time = "2026-01-03T17:31:36.699Z" }, { url = "https://files.pythonhosted.org/packages/c9/6b/915bc5dad66aef602b9e459b5a973529304d4e89ca86999d9d75d80cbd0b/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5e1d8c8b8f1d91cd08d8f4a3c2b067bfca6ec043d3ff36de0f3a715feeedf926", size = 1729968, upload-time = "2026-01-03T17:31:38.622Z" }, { url = "https://files.pythonhosted.org/packages/11/3b/e84581290a9520024a08640b63d07673057aec5ca548177a82026187ba73/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:90455115e5da1c3c51ab619ac57f877da8fd6d73c05aacd125c5ae9819582aba", size = 1545690, upload-time = "2026-01-03T17:31:40.57Z" }, { url = "https://files.pythonhosted.org/packages/f5/04/0c3655a566c43fd647c81b895dfe361b9f9ad6d58c19309d45cff52d6c3b/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:042e9e0bcb5fba81886c8b4fbb9a09d6b8a00245fd8d88e4d989c1f96c74164c", size = 1746390, upload-time = "2026-01-03T17:31:42.857Z" }, { url = "https://files.pythonhosted.org/packages/1f/53/71165b26978f719c3419381514c9690bd5980e764a09440a10bb816ea4ab/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2eb752b102b12a76ca02dff751a801f028b4ffbbc478840b473597fc91a9ed43", size = 1702188, upload-time = "2026-01-03T17:31:44.984Z" }, { url = "https://files.pythonhosted.org/packages/29/a7/cbe6c9e8e136314fa1980da388a59d2f35f35395948a08b6747baebb6aa6/aiohttp-3.13.3-cp314-cp314-win32.whl", hash = "sha256:b556c85915d8efaed322bf1bdae9486aa0f3f764195a0fb6ee962e5c71ef5ce1", size = 433126, upload-time = "2026-01-03T17:31:47.463Z" }, { url = "https://files.pythonhosted.org/packages/de/56/982704adea7d3b16614fc5936014e9af85c0e34b58f9046655817f04306e/aiohttp-3.13.3-cp314-cp314-win_amd64.whl", hash = "sha256:9bf9f7a65e7aa20dd764151fb3d616c81088f91f8df39c3893a536e279b4b984", size = 459128, upload-time = "2026-01-03T17:31:49.2Z" }, { url = "https://files.pythonhosted.org/packages/6c/2a/3c79b638a9c3d4658d345339d22070241ea341ed4e07b5ac60fb0f418003/aiohttp-3.13.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:05861afbbec40650d8a07ea324367cb93e9e8cc7762e04dd4405df99fa65159c", size = 769512, upload-time = "2026-01-03T17:31:51.134Z" }, { url = "https://files.pythonhosted.org/packages/29/b9/3e5014d46c0ab0db8707e0ac2711ed28c4da0218c358a4e7c17bae0d8722/aiohttp-3.13.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2fc82186fadc4a8316768d61f3722c230e2c1dcab4200d52d2ebdf2482e47592", size = 506444, upload-time = "2026-01-03T17:31:52.85Z" }, { url = "https://files.pythonhosted.org/packages/90/03/c1d4ef9a054e151cd7839cdc497f2638f00b93cbe8043983986630d7a80c/aiohttp-3.13.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0add0900ff220d1d5c5ebbf99ed88b0c1bbf87aa7e4262300ed1376a6b13414f", size = 510798, upload-time = "2026-01-03T17:31:54.91Z" }, { url = "https://files.pythonhosted.org/packages/ea/76/8c1e5abbfe8e127c893fe7ead569148a4d5a799f7cf958d8c09f3eedf097/aiohttp-3.13.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:568f416a4072fbfae453dcf9a99194bbb8bdeab718e08ee13dfa2ba0e4bebf29", size = 1868835, upload-time = "2026-01-03T17:31:56.733Z" }, { url = "https://files.pythonhosted.org/packages/8e/ac/984c5a6f74c363b01ff97adc96a3976d9c98940b8969a1881575b279ac5d/aiohttp-3.13.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:add1da70de90a2569c5e15249ff76a631ccacfe198375eead4aadf3b8dc849dc", size = 1720486, upload-time = "2026-01-03T17:31:58.65Z" }, { url = "https://files.pythonhosted.org/packages/b2/9a/b7039c5f099c4eb632138728828b33428585031a1e658d693d41d07d89d1/aiohttp-3.13.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:10b47b7ba335d2e9b1239fa571131a87e2d8ec96b333e68b2a305e7a98b0bae2", size = 1847951, upload-time = "2026-01-03T17:32:00.989Z" }, { url = "https://files.pythonhosted.org/packages/3c/02/3bec2b9a1ba3c19ff89a43a19324202b8eb187ca1e928d8bdac9bbdddebd/aiohttp-3.13.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3dd4dce1c718e38081c8f35f323209d4c1df7d4db4bab1b5c88a6b4d12b74587", size = 1941001, upload-time = "2026-01-03T17:32:03.122Z" }, { url = "https://files.pythonhosted.org/packages/37/df/d879401cedeef27ac4717f6426c8c36c3091c6e9f08a9178cc87549c537f/aiohttp-3.13.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34bac00a67a812570d4a460447e1e9e06fae622946955f939051e7cc895cfab8", size = 1797246, upload-time = "2026-01-03T17:32:05.255Z" }, { url = "https://files.pythonhosted.org/packages/8d/15/be122de1f67e6953add23335c8ece6d314ab67c8bebb3f181063010795a7/aiohttp-3.13.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a19884d2ee70b06d9204b2727a7b9f983d0c684c650254679e716b0b77920632", size = 1627131, upload-time = "2026-01-03T17:32:07.607Z" }, { url = "https://files.pythonhosted.org/packages/12/12/70eedcac9134cfa3219ab7af31ea56bc877395b1ac30d65b1bc4b27d0438/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ca7f2bb6ba8348a3614c7918cc4bb73268c5ac2a207576b7afea19d3d9f64", size = 1795196, upload-time = "2026-01-03T17:32:09.59Z" }, { url = "https://files.pythonhosted.org/packages/32/11/b30e1b1cd1f3054af86ebe60df96989c6a414dd87e27ad16950eee420bea/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:b0d95340658b9d2f11d9697f59b3814a9d3bb4b7a7c20b131df4bcef464037c0", size = 1782841, upload-time = "2026-01-03T17:32:11.445Z" }, { url = "https://files.pythonhosted.org/packages/88/0d/d98a9367b38912384a17e287850f5695c528cff0f14f791ce8ee2e4f7796/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:a1e53262fd202e4b40b70c3aff944a8155059beedc8a89bba9dc1f9ef06a1b56", size = 1795193, upload-time = "2026-01-03T17:32:13.705Z" }, { url = "https://files.pythonhosted.org/packages/43/a5/a2dfd1f5ff5581632c7f6a30e1744deda03808974f94f6534241ef60c751/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d60ac9663f44168038586cab2157e122e46bdef09e9368b37f2d82d354c23f72", size = 1621979, upload-time = "2026-01-03T17:32:15.965Z" }, { url = "https://files.pythonhosted.org/packages/fa/f0/12973c382ae7c1cccbc4417e129c5bf54c374dfb85af70893646e1f0e749/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:90751b8eed69435bac9ff4e3d2f6b3af1f57e37ecb0fbeee59c0174c9e2d41df", size = 1822193, upload-time = "2026-01-03T17:32:18.219Z" }, { url = "https://files.pythonhosted.org/packages/3c/5f/24155e30ba7f8c96918af1350eb0663e2430aad9e001c0489d89cd708ab1/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fc353029f176fd2b3ec6cfc71be166aba1936fe5d73dd1992ce289ca6647a9aa", size = 1769801, upload-time = "2026-01-03T17:32:20.25Z" }, { url = "https://files.pythonhosted.org/packages/eb/f8/7314031ff5c10e6ece114da79b338ec17eeff3a079e53151f7e9f43c4723/aiohttp-3.13.3-cp314-cp314t-win32.whl", hash = "sha256:2e41b18a58da1e474a057b3d35248d8320029f61d70a37629535b16a0c8f3767", size = 466523, upload-time = "2026-01-03T17:32:22.215Z" }, { url = "https://files.pythonhosted.org/packages/b4/63/278a98c715ae467624eafe375542d8ba9b4383a016df8fdefe0ae28382a7/aiohttp-3.13.3-cp314-cp314t-win_amd64.whl", hash = "sha256:44531a36aa2264a1860089ffd4dce7baf875ee5a6079d5fb42e261c704ef7344", size = 499694, upload-time = "2026-01-03T17:32:24.546Z" }, { url = "https://files.pythonhosted.org/packages/bf/79/446655656861d3e7e2c32bfcf160c7aa9e9dc63776a691b124dba65cdd77/aiohttp-3.13.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:31a83ea4aead760dfcb6962efb1d861db48c34379f2ff72db9ddddd4cda9ea2e", size = 741433, upload-time = "2026-01-03T17:32:26.453Z" }, { url = "https://files.pythonhosted.org/packages/cb/49/773c4b310b5140d2fb5e79bb0bf40b7b41dad80a288ca1a8759f5f72bda9/aiohttp-3.13.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:988a8c5e317544fdf0d39871559e67b6341065b87fceac641108c2096d5506b7", size = 497332, upload-time = "2026-01-03T17:32:28.37Z" }, { url = "https://files.pythonhosted.org/packages/bc/31/1dcbc4b83a4e6f76a0ad883f07f21ffbfe29750c89db97381701508c9f45/aiohttp-3.13.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9b174f267b5cfb9a7dba9ee6859cecd234e9a681841eb85068059bc867fb8f02", size = 492365, upload-time = "2026-01-03T17:32:30.234Z" }, { url = "https://files.pythonhosted.org/packages/5a/b5/b50657496c8754482cd7964e50aaf3aa84b3db61ed45daec4c1aec5b94b4/aiohttp-3.13.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:947c26539750deeaee933b000fb6517cc770bbd064bad6033f1cff4803881e43", size = 1660440, upload-time = "2026-01-03T17:32:32.586Z" }, { url = "https://files.pythonhosted.org/packages/2a/73/9b69e5139d89d75127569298931444ad78ea86a5befd5599780b1e9a6880/aiohttp-3.13.3-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9ebf57d09e131f5323464bd347135a88622d1c0976e88ce15b670e7ad57e4bd6", size = 1632740, upload-time = "2026-01-03T17:32:34.793Z" }, { url = "https://files.pythonhosted.org/packages/ef/fe/3ea9b5af694b4e3aec0d0613a806132ca744747146fca68e96bf056f61a7/aiohttp-3.13.3-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4ae5b5a0e1926e504c81c5b84353e7a5516d8778fbbff00429fe7b05bb25cbce", size = 1719782, upload-time = "2026-01-03T17:32:37.737Z" }, { url = "https://files.pythonhosted.org/packages/fb/c2/46b3b06e60851cbb71efb0f79a3267279cbef7b12c58e68a1e897f269cca/aiohttp-3.13.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2ba0eea45eb5cc3172dbfc497c066f19c41bac70963ea1a67d51fc92e4cf9a80", size = 1813527, upload-time = "2026-01-03T17:32:39.973Z" }, { url = "https://files.pythonhosted.org/packages/36/23/71ceb78c769ed65fe4c697692de232b63dab399210678d2b00961ccb0619/aiohttp-3.13.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bae5c2ed2eae26cc382020edad80d01f36cb8e746da40b292e68fec40421dc6a", size = 1661268, upload-time = "2026-01-03T17:32:42.082Z" }, { url = "https://files.pythonhosted.org/packages/c4/8d/86e929523d955e85ebab7c0e2b9e0cb63604cfc27dc3280e10d0063cf682/aiohttp-3.13.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8a60e60746623925eab7d25823329941aee7242d559baa119ca2b253c88a7bd6", size = 1552742, upload-time = "2026-01-03T17:32:44.622Z" }, { url = "https://files.pythonhosted.org/packages/3a/ea/3f5987cba1bab6bd151f0d97aa60f0ce04d3c83316692a6bb6ba2fb69f92/aiohttp-3.13.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e50a2e1404f063427c9d027378472316201a2290959a295169bcf25992d04558", size = 1632918, upload-time = "2026-01-03T17:32:46.749Z" }, { url = "https://files.pythonhosted.org/packages/be/2c/7e1e85121f2e31ee938cb83a8f32dfafd4908530c10fabd6d46761c12ac7/aiohttp-3.13.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:9a9dc347e5a3dc7dfdbc1f82da0ef29e388ddb2ed281bfce9dd8248a313e62b7", size = 1644446, upload-time = "2026-01-03T17:32:49.063Z" }, { url = "https://files.pythonhosted.org/packages/5d/35/ce6133d423ad0e8ca976a7c848f7146bca3520eea4ccf6b95e2d077c9d20/aiohttp-3.13.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b46020d11d23fe16551466c77823df9cc2f2c1e63cc965daf67fa5eec6ca1877", size = 1689487, upload-time = "2026-01-03T17:32:51.113Z" }, { url = "https://files.pythonhosted.org/packages/50/f7/ff7a27c15603d460fd1366b3c22054f7ae4fa9310aca40b43bde35867fcd/aiohttp-3.13.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:69c56fbc1993fa17043e24a546959c0178fe2b5782405ad4559e6c13975c15e3", size = 1540715, upload-time = "2026-01-03T17:32:53.38Z" }, { url = "https://files.pythonhosted.org/packages/17/02/053f11346e5b962e6d8a1c4f8c70c29d5970a1b4b8e7894c68e12c27a57f/aiohttp-3.13.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:b99281b0704c103d4e11e72a76f1b543d4946fea7dd10767e7e1b5f00d4e5704", size = 1711835, upload-time = "2026-01-03T17:32:56.088Z" }, { url = "https://files.pythonhosted.org/packages/fb/71/9b9761ddf276fd6708d13720197cbac19b8d67ecfa9116777924056cfcaa/aiohttp-3.13.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:40c5e40ecc29ba010656c18052b877a1c28f84344825efa106705e835c28530f", size = 1649593, upload-time = "2026-01-03T17:32:58.181Z" }, { url = "https://files.pythonhosted.org/packages/ae/72/5d817e9ea218acae12a5e3b9ad1178cf0c12fc3570c0b47eea2daf95f9ea/aiohttp-3.13.3-cp39-cp39-win32.whl", hash = "sha256:56339a36b9f1fc708260c76c87e593e2afb30d26de9ae1eb445b5e051b98a7a1", size = 434831, upload-time = "2026-01-03T17:33:00.577Z" }, { url = "https://files.pythonhosted.org/packages/39/cb/22659d9bf3149b7a2927bc2769cc9c8f8f5a80eba098398e03c199a43a85/aiohttp-3.13.3-cp39-cp39-win_amd64.whl", hash = "sha256:c6b8568a3bb5819a0ad087f16d40e5a3fb6099f39ea1d5625a3edc1e923fc538", size = 457697, upload-time = "2026-01-03T17:33:03.167Z" }, ] [[package]] name = "aiosignal" version = "1.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "frozenlist" }, { name = "typing-extensions", marker = "python_full_version < '3.13' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, ] [[package]] name = "annotated-types" version = "0.7.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } 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 = { editable = "." } dependencies = [ { name = "anyio" }, { name = "distro" }, { name = "docstring-parser" }, { name = "httpx" }, { name = "jiter" }, { name = "pydantic", version = "1.10.26", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'group-9-anthropic-pydantic-v1'" }, { name = "pydantic", version = "2.12.5", source = { registry = "https://pypi.org/simple" }, marker = "extra == 'extra-9-anthropic-mcp' or extra != 'group-9-anthropic-pydantic-v1' or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "sniffio" }, { name = "typing-extensions" }, ] [package.optional-dependencies] aiohttp = [ { name = "aiohttp" }, { name = "httpx-aiohttp" }, ] aws = [ { name = "boto3" }, { name = "botocore" }, ] bedrock = [ { name = "boto3" }, { name = "botocore" }, ] google-cloud = [ { name = "google-auth", extra = ["requests"] }, ] mcp = [ { name = "mcp", marker = "python_full_version >= '3.10'" }, ] vertex = [ { name = "google-auth", extra = ["requests"] }, ] webhooks = [ { name = "standardwebhooks" }, ] [package.dev-dependencies] dev = [ { name = "boto3-stubs" }, { name = "dirty-equals" }, { name = "griffe", version = "1.14.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "griffe", version = "2.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "http-snapshot", extra = ["httpx"] }, { name = "importlib-metadata" }, { name = "inline-snapshot" }, { name = "mypy" }, { name = "pyright" }, { name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "pytest", version = "9.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "pytest-asyncio", version = "1.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "pytest-asyncio", version = "1.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "pytest-xdist" }, { name = "respx" }, { name = "rich" }, { name = "ruff" }, { name = "time-machine", version = "2.19.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "time-machine", version = "3.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] pydantic-v1 = [ { name = "pydantic", version = "1.10.26", source = { registry = "https://pypi.org/simple" } }, ] pydantic-v2 = [ { name = "pydantic", version = "2.12.5", source = { registry = "https://pypi.org/simple" } }, ] [package.metadata] requires-dist = [ { name = "aiohttp", marker = "extra == 'aiohttp'" }, { name = "anyio", specifier = ">=3.5.0,<5" }, { name = "boto3", marker = "extra == 'aws'", specifier = ">=1.28.57" }, { name = "boto3", marker = "extra == 'bedrock'", specifier = ">=1.28.57" }, { name = "botocore", marker = "extra == 'aws'", specifier = ">=1.31.57" }, { name = "botocore", marker = "extra == 'bedrock'", specifier = ">=1.31.57" }, { name = "distro", specifier = ">=1.7.0,<2" }, { name = "docstring-parser", specifier = ">=0.15,<1" }, { name = "google-auth", extras = ["requests"], marker = "extra == 'google-cloud'", specifier = ">=2,<3" }, { name = "google-auth", extras = ["requests"], marker = "extra == 'vertex'", specifier = ">=2,<3" }, { name = "httpx", specifier = ">=0.25.0,<1" }, { name = "httpx-aiohttp", marker = "extra == 'aiohttp'", specifier = ">=0.1.9,<1" }, { name = "jiter", specifier = ">=0.4.0,<1" }, { name = "mcp", marker = "python_full_version >= '3.10' and extra == 'mcp'", specifier = ">=1.0,<3" }, { name = "pydantic", specifier = ">=1.9.0,<3" }, { name = "sniffio" }, { name = "standardwebhooks", marker = "extra == 'webhooks'", specifier = ">=1.0.1,<2" }, { name = "typing-extensions", specifier = ">=4.14,<5" }, ] provides-extras = ["aiohttp", "vertex", "google-cloud", "aws", "bedrock", "mcp", "webhooks"] [package.metadata.requires-dev] dev = [ { name = "boto3-stubs", specifier = ">=1" }, { name = "dirty-equals", specifier = ">=0.6.0" }, { name = "griffe", specifier = ">=1" }, { name = "http-snapshot", extras = ["httpx"], specifier = "==0.1.9" }, { name = "importlib-metadata", specifier = ">=6.7.0" }, { name = "inline-snapshot", specifier = ">=0.28.0" }, { name = "mypy", specifier = "==1.17" }, { name = "pyright", specifier = "==1.1.399" }, { name = "pytest" }, { name = "pytest-asyncio" }, { name = "pytest-xdist", specifier = ">=3.6.1" }, { name = "respx" }, { name = "rich", specifier = ">=13.7.1" }, { name = "ruff" }, { name = "time-machine" }, ] pydantic-v1 = [{ name = "pydantic", specifier = ">=1.9.0,<2" }] pydantic-v2 = [ { name = "pydantic", marker = "python_full_version < '3.14'", specifier = "~=2.0" }, { name = "pydantic", marker = "python_full_version >= '3.14'", specifier = "~=2.12" }, ] [[package]] name = "anyio" version = "4.12.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "exceptiongroup", marker = "python_full_version < '3.11' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "idna" }, { name = "typing-extensions", marker = "python_full_version < '3.13' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, ] [[package]] name = "asttokens" version = "3.0.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308, upload-time = "2025-11-15T16:43:48.578Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047, upload-time = "2025-11-15T16:43:16.109Z" }, ] [[package]] name = "async-timeout" version = "5.0.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274, upload-time = "2024-11-06T16:41:39.6Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" }, ] [[package]] name = "attrs" version = "25.4.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, ] [[package]] name = "backports-asyncio-runner" version = "1.2.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/8e/ff/70dca7d7cb1cbc0edb2c6cc0c38b65cba36cccc491eca64cabd5fe7f8670/backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162", size = 69893, upload-time = "2025-07-02T02:27:15.685Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" }, ] [[package]] name = "boto3" version = "1.42.69" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, { name = "jmespath" }, { name = "s3transfer" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1f/f3/26d800e4efe85e7d59c63ac11d02ab2fafed371bede567af7258eb7e4c1c/boto3-1.42.69.tar.gz", hash = "sha256:e59846f4ff467b23bae4751948298db554dbdda0d72b09028d2cacbeff27e1ad", size = 112777, upload-time = "2026-03-16T20:35:30.77Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/f3/39/54ad87436c637de9f7bf83ba2a28cf3b15409cbb849401837fcc37fbd794/boto3-1.42.69-py3-none-any.whl", hash = "sha256:6823a4b59aa578c7d98124280a9b6d83cea04bdb02525cbaa79370e5b6f7f631", size = 140556, upload-time = "2026-03-16T20:35:28.754Z" }, ] [[package]] name = "boto3-stubs" version = "1.42.69" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore-stubs" }, { name = "types-s3transfer" }, { name = "typing-extensions", marker = "python_full_version < '3.12' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1e/ba/b282b7ab3626a25a6896c2f31adc95324b3e5f50056923d274a35c5eaf0c/boto3_stubs-1.42.69.tar.gz", hash = "sha256:52ccd645a34d2b4e97af8f44dbaffbb854a1de52610e9502c284bfb24e6d8962", size = 101397, upload-time = "2026-03-16T20:58:58.538Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/6a/78/83ef6f549d88425618ce66d4b273ea46e379aefdf0e9e49bf4f9bfa01cda/boto3_stubs-1.42.69-py3-none-any.whl", hash = "sha256:021360b519ac54822eb00f125b0c4292ad2a1869ae8e1d0c6c097db99215d41b", size = 70010, upload-time = "2026-03-16T20:58:51.184Z" }, ] [[package]] name = "botocore" version = "1.42.69" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jmespath" }, { name = "python-dateutil" }, { name = "urllib3", version = "1.26.20", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "urllib3", version = "2.6.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/42/d1/81a6e39c7d5419ba34bad8a1ac2c5360c26f21af698a481a8397d79134d1/botocore-1.42.69.tar.gz", hash = "sha256:0934f2d90403c5c8c2cba83e754a39d77edcad5885d04a79363edff3e814f55e", size = 14997632, upload-time = "2026-03-16T20:35:18.533Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/f6/13/779f3427e17f9989fd0fa6651817c5f13b63e574f3541e460b8238883290/botocore-1.42.69-py3-none-any.whl", hash = "sha256:ef0e3d860a5d7bffc0ccb4911781c4c27d538557ed9a616ba1926c762d72e5f6", size = 14670334, upload-time = "2026-03-16T20:35:14.543Z" }, ] [[package]] name = "botocore-stubs" version = "1.42.41" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "types-awscrt" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0c/a8/a26608ff39e3a5866c6c79eda10133490205cbddd45074190becece3ff2a/botocore_stubs-1.42.41.tar.gz", hash = "sha256:dbeac2f744df6b814ce83ec3f3777b299a015cbea57a2efc41c33b8c38265825", size = 42411, upload-time = "2026-02-03T20:46:14.479Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/32/76/cab7af7f16c0b09347f2ebe7ffda7101132f786acb767666dce43055faab/botocore_stubs-1.42.41-py3-none-any.whl", hash = "sha256:9423110fb0e391834bd2ed44ae5f879d8cb370a444703d966d30842ce2bcb5f0", size = 66759, upload-time = "2026-02-03T20:46:13.02Z" }, ] [[package]] name = "certifi" version = "2026.2.25" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, ] [[package]] name = "cffi" version = "2.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pycparser", version = "2.23", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.10' and implementation_name != 'PyPy') or (python_full_version >= '3.10' and extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (python_full_version >= '3.10' and extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2') or (implementation_name == 'PyPy' and extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (implementation_name == 'PyPy' and extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "pycparser", version = "3.0", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.10' and implementation_name != 'PyPy') or (python_full_version < '3.10' and extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (python_full_version < '3.10' and extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2') or (implementation_name == 'PyPy' and extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (implementation_name == 'PyPy' and extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] 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" }, { url = "https://files.pythonhosted.org/packages/c0/cc/08ed5a43f2996a16b462f64a7055c6e962803534924b9b2f1371d8c00b7b/cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf", size = 184288, upload-time = "2025-09-08T23:23:48.404Z" }, { url = "https://files.pythonhosted.org/packages/3d/de/38d9726324e127f727b4ecc376bc85e505bfe61ef130eaf3f290c6847dd4/cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7", size = 180509, upload-time = "2025-09-08T23:23:49.73Z" }, { url = "https://files.pythonhosted.org/packages/9b/13/c92e36358fbcc39cf0962e83223c9522154ee8630e1df7c0b3a39a8124e2/cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c", size = 208813, upload-time = "2025-09-08T23:23:51.263Z" }, { url = "https://files.pythonhosted.org/packages/15/12/a7a79bd0df4c3bff744b2d7e52cc1b68d5e7e427b384252c42366dc1ecbc/cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165", size = 216498, upload-time = "2025-09-08T23:23:52.494Z" }, { url = "https://files.pythonhosted.org/packages/a3/ad/5c51c1c7600bdd7ed9a24a203ec255dccdd0ebf4527f7b922a0bde2fb6ed/cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534", size = 203243, upload-time = "2025-09-08T23:23:53.836Z" }, { url = "https://files.pythonhosted.org/packages/32/f2/81b63e288295928739d715d00952c8c6034cb6c6a516b17d37e0c8be5600/cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f", size = 203158, upload-time = "2025-09-08T23:23:55.169Z" }, { url = "https://files.pythonhosted.org/packages/1f/74/cc4096ce66f5939042ae094e2e96f53426a979864aa1f96a621ad128be27/cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63", size = 216548, upload-time = "2025-09-08T23:23:56.506Z" }, { url = "https://files.pythonhosted.org/packages/e8/be/f6424d1dc46b1091ffcc8964fa7c0ab0cd36839dd2761b49c90481a6ba1b/cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2", size = 218897, upload-time = "2025-09-08T23:23:57.825Z" }, { url = "https://files.pythonhosted.org/packages/f7/e0/dda537c2309817edf60109e39265f24f24aa7f050767e22c98c53fe7f48b/cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65", size = 211249, upload-time = "2025-09-08T23:23:59.139Z" }, { url = "https://files.pythonhosted.org/packages/2b/e7/7c769804eb75e4c4b35e658dba01de1640a351a9653c3d49ca89d16ccc91/cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322", size = 218041, upload-time = "2025-09-08T23:24:00.496Z" }, { url = "https://files.pythonhosted.org/packages/aa/d9/6218d78f920dcd7507fc16a766b5ef8f3b913cc7aa938e7fc80b9978d089/cffi-2.0.0-cp39-cp39-win32.whl", hash = "sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a", size = 172138, upload-time = "2025-09-08T23:24:01.7Z" }, { url = "https://files.pythonhosted.org/packages/54/8f/a1e836f82d8e32a97e6b29cc8f641779181ac7363734f12df27db803ebda/cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9", size = 182794, upload-time = "2025-09-08T23:24:02.943Z" }, ] [[package]] name = "charset-normalizer" version = "3.4.6" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/7b/60/e3bec1881450851b087e301bedc3daa9377a4d45f1c26aa90b0b235e38aa/charset_normalizer-3.4.6.tar.gz", hash = "sha256:1ae6b62897110aa7c79ea2f5dd38d1abca6db663687c0b1ad9aed6f6bae3d9d6", size = 143363, upload-time = "2026-03-15T18:53:25.478Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/e6/8c/2c56124c6dc53a774d435f985b5973bc592f42d437be58c0c92d65ae7296/charset_normalizer-3.4.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2e1d8ca8611099001949d1cdfaefc510cf0f212484fe7c565f735b68c78c3c95", size = 298751, upload-time = "2026-03-15T18:50:00.003Z" }, { url = "https://files.pythonhosted.org/packages/86/2a/2a7db6b314b966a3bcad8c731c0719c60b931b931de7ae9f34b2839289ee/charset_normalizer-3.4.6-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e25369dc110d58ddf29b949377a93e0716d72a24f62bad72b2b39f155949c1fd", size = 200027, upload-time = "2026-03-15T18:50:01.702Z" }, { url = "https://files.pythonhosted.org/packages/68/f2/0fe775c74ae25e2a3b07b01538fc162737b3e3f795bada3bc26f4d4d495c/charset_normalizer-3.4.6-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:259695e2ccc253feb2a016303543d691825e920917e31f894ca1a687982b1de4", size = 220741, upload-time = "2026-03-15T18:50:03.194Z" }, { url = "https://files.pythonhosted.org/packages/10/98/8085596e41f00b27dd6aa1e68413d1ddda7e605f34dd546833c61fddd709/charset_normalizer-3.4.6-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:dda86aba335c902b6149a02a55b38e96287157e609200811837678214ba2b1db", size = 215802, upload-time = "2026-03-15T18:50:05.859Z" }, { url = "https://files.pythonhosted.org/packages/fd/ce/865e4e09b041bad659d682bbd98b47fb490b8e124f9398c9448065f64fee/charset_normalizer-3.4.6-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51fb3c322c81d20567019778cb5a4a6f2dc1c200b886bc0d636238e364848c89", size = 207908, upload-time = "2026-03-15T18:50:07.676Z" }, { url = "https://files.pythonhosted.org/packages/a8/54/8c757f1f7349262898c2f169e0d562b39dcb977503f18fdf0814e923db78/charset_normalizer-3.4.6-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:4482481cb0572180b6fd976a4d5c72a30263e98564da68b86ec91f0fe35e8565", size = 194357, upload-time = "2026-03-15T18:50:09.327Z" }, { url = "https://files.pythonhosted.org/packages/6f/29/e88f2fac9218907fc7a70722b393d1bbe8334c61fe9c46640dba349b6e66/charset_normalizer-3.4.6-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:39f5068d35621da2881271e5c3205125cc456f54e9030d3f723288c873a71bf9", size = 205610, upload-time = "2026-03-15T18:50:10.732Z" }, { url = "https://files.pythonhosted.org/packages/4c/c5/21d7bb0cb415287178450171d130bed9d664211fdd59731ed2c34267b07d/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8bea55c4eef25b0b19a0337dc4e3f9a15b00d569c77211fa8cde38684f234fb7", size = 203512, upload-time = "2026-03-15T18:50:12.535Z" }, { url = "https://files.pythonhosted.org/packages/a4/be/ce52f3c7fdb35cc987ad38a53ebcef52eec498f4fb6c66ecfe62cfe57ba2/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:f0cdaecd4c953bfae0b6bb64910aaaca5a424ad9c72d85cb88417bb9814f7550", size = 195398, upload-time = "2026-03-15T18:50:14.236Z" }, { url = "https://files.pythonhosted.org/packages/81/a0/3ab5dd39d4859a3555e5dadfc8a9fa7f8352f8c183d1a65c90264517da0e/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:150b8ce8e830eb7ccb029ec9ca36022f756986aaaa7956aad6d9ec90089338c0", size = 221772, upload-time = "2026-03-15T18:50:15.581Z" }, { url = "https://files.pythonhosted.org/packages/04/6e/6a4e41a97ba6b2fa87f849c41e4d229449a586be85053c4d90135fe82d26/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:e68c14b04827dd76dcbd1aeea9e604e3e4b78322d8faf2f8132c7138efa340a8", size = 205759, upload-time = "2026-03-15T18:50:17.047Z" }, { url = "https://files.pythonhosted.org/packages/db/3b/34a712a5ee64a6957bf355b01dc17b12de457638d436fdb05d01e463cd1c/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:3778fd7d7cd04ae8f54651f4a7a0bd6e39a0cf20f801720a4c21d80e9b7ad6b0", size = 216938, upload-time = "2026-03-15T18:50:18.44Z" }, { url = "https://files.pythonhosted.org/packages/cb/05/5bd1e12da9ab18790af05c61aafd01a60f489778179b621ac2a305243c62/charset_normalizer-3.4.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:dad6e0f2e481fffdcf776d10ebee25e0ef89f16d691f1e5dee4b586375fdc64b", size = 210138, upload-time = "2026-03-15T18:50:19.852Z" }, { url = "https://files.pythonhosted.org/packages/bd/8e/3cb9e2d998ff6b21c0a1860343cb7b83eba9cdb66b91410e18fc4969d6ab/charset_normalizer-3.4.6-cp310-cp310-win32.whl", hash = "sha256:74a2e659c7ecbc73562e2a15e05039f1e22c75b7c7618b4b574a3ea9118d1557", size = 144137, upload-time = "2026-03-15T18:50:21.505Z" }, { url = "https://files.pythonhosted.org/packages/d8/8f/78f5489ffadb0db3eb7aff53d31c24531d33eb545f0c6f6567c25f49a5ff/charset_normalizer-3.4.6-cp310-cp310-win_amd64.whl", hash = "sha256:aa9cccf4a44b9b62d8ba8b4dd06c649ba683e4bf04eea606d2e94cfc2d6ff4d6", size = 154244, upload-time = "2026-03-15T18:50:22.81Z" }, { url = "https://files.pythonhosted.org/packages/e4/74/e472659dffb0cadb2f411282d2d76c60da1fc94076d7fffed4ae8a93ec01/charset_normalizer-3.4.6-cp310-cp310-win_arm64.whl", hash = "sha256:e985a16ff513596f217cee86c21371b8cd011c0f6f056d0920aa2d926c544058", size = 143312, upload-time = "2026-03-15T18:50:24.074Z" }, { url = "https://files.pythonhosted.org/packages/62/28/ff6f234e628a2de61c458be2779cb182bc03f6eec12200d4a525bbfc9741/charset_normalizer-3.4.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:82060f995ab5003a2d6e0f4ad29065b7672b6593c8c63559beefe5b443242c3e", size = 293582, upload-time = "2026-03-15T18:50:25.454Z" }, { url = "https://files.pythonhosted.org/packages/1c/b7/b1a117e5385cbdb3205f6055403c2a2a220c5ea80b8716c324eaf75c5c95/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:60c74963d8350241a79cb8feea80e54d518f72c26db618862a8f53e5023deaf9", size = 197240, upload-time = "2026-03-15T18:50:27.196Z" }, { url = "https://files.pythonhosted.org/packages/a1/5f/2574f0f09f3c3bc1b2f992e20bce6546cb1f17e111c5be07308dc5427956/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6e4333fb15c83f7d1482a76d45a0818897b3d33f00efd215528ff7c51b8e35d", size = 217363, upload-time = "2026-03-15T18:50:28.601Z" }, { url = "https://files.pythonhosted.org/packages/4a/d1/0ae20ad77bc949ddd39b51bf383b6ca932f2916074c95cad34ae465ab71f/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bc72863f4d9aba2e8fd9085e63548a324ba706d2ea2c83b260da08a59b9482de", size = 212994, upload-time = "2026-03-15T18:50:30.102Z" }, { url = "https://files.pythonhosted.org/packages/60/ac/3233d262a310c1b12633536a07cde5ddd16985e6e7e238e9f3f9423d8eb9/charset_normalizer-3.4.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9cc4fc6c196d6a8b76629a70ddfcd4635a6898756e2d9cac5565cf0654605d73", size = 204697, upload-time = "2026-03-15T18:50:31.654Z" }, { url = "https://files.pythonhosted.org/packages/25/3c/8a18fc411f085b82303cfb7154eed5bd49c77035eb7608d049468b53f87c/charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:0c173ce3a681f309f31b87125fecec7a5d1347261ea11ebbb856fa6006b23c8c", size = 191673, upload-time = "2026-03-15T18:50:33.433Z" }, { url = "https://files.pythonhosted.org/packages/ff/a7/11cfe61d6c5c5c7438d6ba40919d0306ed83c9ab957f3d4da2277ff67836/charset_normalizer-3.4.6-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c907cdc8109f6c619e6254212e794d6548373cc40e1ec75e6e3823d9135d29cc", size = 201120, upload-time = "2026-03-15T18:50:35.105Z" }, { url = "https://files.pythonhosted.org/packages/b5/10/cf491fa1abd47c02f69687046b896c950b92b6cd7337a27e6548adbec8e4/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:404a1e552cf5b675a87f0651f8b79f5f1e6fd100ee88dc612f89aa16abd4486f", size = 200911, upload-time = "2026-03-15T18:50:36.819Z" }, { url = "https://files.pythonhosted.org/packages/28/70/039796160b48b18ed466fde0af84c1b090c4e288fae26cd674ad04a2d703/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e3c701e954abf6fc03a49f7c579cc80c2c6cc52525340ca3186c41d3f33482ef", size = 192516, upload-time = "2026-03-15T18:50:38.228Z" }, { url = "https://files.pythonhosted.org/packages/ff/34/c56f3223393d6ff3124b9e78f7de738047c2d6bc40a4f16ac0c9d7a1cb3c/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7a6967aaf043bceabab5412ed6bd6bd26603dae84d5cb75bf8d9a74a4959d398", size = 218795, upload-time = "2026-03-15T18:50:39.664Z" }, { url = "https://files.pythonhosted.org/packages/e8/3b/ce2d4f86c5282191a041fdc5a4ce18f1c6bd40a5bd1f74cf8625f08d51c1/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5feb91325bbceade6afab43eb3b508c63ee53579fe896c77137ded51c6b6958e", size = 201833, upload-time = "2026-03-15T18:50:41.552Z" }, { url = "https://files.pythonhosted.org/packages/3b/9b/b6a9f76b0fd7c5b5ec58b228ff7e85095370282150f0bd50b3126f5506d6/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f820f24b09e3e779fe84c3c456cb4108a7aa639b0d1f02c28046e11bfcd088ed", size = 213920, upload-time = "2026-03-15T18:50:43.33Z" }, { url = "https://files.pythonhosted.org/packages/ae/98/7bc23513a33d8172365ed30ee3a3b3fe1ece14a395e5fc94129541fc6003/charset_normalizer-3.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b35b200d6a71b9839a46b9b7fff66b6638bb52fc9658aa58796b0326595d3021", size = 206951, upload-time = "2026-03-15T18:50:44.789Z" }, { url = "https://files.pythonhosted.org/packages/32/73/c0b86f3d1458468e11aec870e6b3feac931facbe105a894b552b0e518e79/charset_normalizer-3.4.6-cp311-cp311-win32.whl", hash = "sha256:9ca4c0b502ab399ef89248a2c84c54954f77a070f28e546a85e91da627d1301e", size = 143703, upload-time = "2026-03-15T18:50:46.103Z" }, { url = "https://files.pythonhosted.org/packages/c6/e3/76f2facfe8eddee0bbd38d2594e709033338eae44ebf1738bcefe0a06185/charset_normalizer-3.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:a9e68c9d88823b274cf1e72f28cb5dc89c990edf430b0bfd3e2fb0785bfeabf4", size = 153857, upload-time = "2026-03-15T18:50:47.563Z" }, { url = "https://files.pythonhosted.org/packages/e2/dc/9abe19c9b27e6cd3636036b9d1b387b78c40dedbf0b47f9366737684b4b0/charset_normalizer-3.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:97d0235baafca5f2b09cf332cc275f021e694e8362c6bb9c96fc9a0eb74fc316", size = 142751, upload-time = "2026-03-15T18:50:49.234Z" }, { url = "https://files.pythonhosted.org/packages/e5/62/c0815c992c9545347aeea7859b50dc9044d147e2e7278329c6e02ac9a616/charset_normalizer-3.4.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2ef7fedc7a6ecbe99969cd09632516738a97eeb8bd7258bf8a0f23114c057dab", size = 295154, upload-time = "2026-03-15T18:50:50.88Z" }, { url = "https://files.pythonhosted.org/packages/a8/37/bdca6613c2e3c58c7421891d80cc3efa1d32e882f7c4a7ee6039c3fc951a/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a4ea868bc28109052790eb2b52a9ab33f3aa7adc02f96673526ff47419490e21", size = 199191, upload-time = "2026-03-15T18:50:52.658Z" }, { url = "https://files.pythonhosted.org/packages/6c/92/9934d1bbd69f7f398b38c5dae1cbf9cc672e7c34a4adf7b17c0a9c17d15d/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:836ab36280f21fc1a03c99cd05c6b7af70d2697e374c7af0b61ed271401a72a2", size = 218674, upload-time = "2026-03-15T18:50:54.102Z" }, { url = "https://files.pythonhosted.org/packages/af/90/25f6ab406659286be929fd89ab0e78e38aa183fc374e03aa3c12d730af8a/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f1ce721c8a7dfec21fcbdfe04e8f68174183cf4e8188e0645e92aa23985c57ff", size = 215259, upload-time = "2026-03-15T18:50:55.616Z" }, { url = "https://files.pythonhosted.org/packages/4e/ef/79a463eb0fff7f96afa04c1d4c51f8fc85426f918db467854bfb6a569ce3/charset_normalizer-3.4.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e28d62a8fc7a1fa411c43bd65e346f3bce9716dc51b897fbe930c5987b402d5", size = 207276, upload-time = "2026-03-15T18:50:57.054Z" }, { url = "https://files.pythonhosted.org/packages/f7/72/d0426afec4b71dc159fa6b4e68f868cd5a3ecd918fec5813a15d292a7d10/charset_normalizer-3.4.6-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:530d548084c4a9f7a16ed4a294d459b4f229db50df689bfe92027452452943a0", size = 195161, upload-time = "2026-03-15T18:50:58.686Z" }, { url = "https://files.pythonhosted.org/packages/bf/18/c82b06a68bfcb6ce55e508225d210c7e6a4ea122bfc0748892f3dc4e8e11/charset_normalizer-3.4.6-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:30f445ae60aad5e1f8bdbb3108e39f6fbc09f4ea16c815c66578878325f8f15a", size = 203452, upload-time = "2026-03-15T18:51:00.196Z" }, { url = "https://files.pythonhosted.org/packages/44/d6/0c25979b92f8adafdbb946160348d8d44aa60ce99afdc27df524379875cb/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ac2393c73378fea4e52aa56285a3d64be50f1a12395afef9cce47772f60334c2", size = 202272, upload-time = "2026-03-15T18:51:01.703Z" }, { url = "https://files.pythonhosted.org/packages/2e/3d/7fea3e8fe84136bebbac715dd1221cc25c173c57a699c030ab9b8900cbb7/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:90ca27cd8da8118b18a52d5f547859cc1f8354a00cd1e8e5120df3e30d6279e5", size = 195622, upload-time = "2026-03-15T18:51:03.526Z" }, { url = "https://files.pythonhosted.org/packages/57/8a/d6f7fd5cb96c58ef2f681424fbca01264461336d2a7fc875e4446b1f1346/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e5a94886bedca0f9b78fecd6afb6629142fd2605aa70a125d49f4edc6037ee6", size = 220056, upload-time = "2026-03-15T18:51:05.269Z" }, { url = "https://files.pythonhosted.org/packages/16/50/478cdda782c8c9c3fb5da3cc72dd7f331f031e7f1363a893cdd6ca0f8de0/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:695f5c2823691a25f17bc5d5ffe79fa90972cc34b002ac6c843bb8a1720e950d", size = 203751, upload-time = "2026-03-15T18:51:06.858Z" }, { url = "https://files.pythonhosted.org/packages/75/fc/cc2fcac943939c8e4d8791abfa139f685e5150cae9f94b60f12520feaa9b/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:231d4da14bcd9301310faf492051bee27df11f2bc7549bc0bb41fef11b82daa2", size = 216563, upload-time = "2026-03-15T18:51:08.564Z" }, { url = "https://files.pythonhosted.org/packages/a8/b7/a4add1d9a5f68f3d037261aecca83abdb0ab15960a3591d340e829b37298/charset_normalizer-3.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a056d1ad2633548ca18ffa2f85c202cfb48b68615129143915b8dc72a806a923", size = 209265, upload-time = "2026-03-15T18:51:10.312Z" }, { url = "https://files.pythonhosted.org/packages/6c/18/c094561b5d64a24277707698e54b7f67bd17a4f857bbfbb1072bba07c8bf/charset_normalizer-3.4.6-cp312-cp312-win32.whl", hash = "sha256:c2274ca724536f173122f36c98ce188fd24ce3dad886ec2b7af859518ce008a4", size = 144229, upload-time = "2026-03-15T18:51:11.694Z" }, { url = "https://files.pythonhosted.org/packages/ab/20/0567efb3a8fd481b8f34f739ebddc098ed062a59fed41a8d193a61939e8f/charset_normalizer-3.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:c8ae56368f8cc97c7e40a7ee18e1cedaf8e780cd8bc5ed5ac8b81f238614facb", size = 154277, upload-time = "2026-03-15T18:51:13.004Z" }, { url = "https://files.pythonhosted.org/packages/15/57/28d79b44b51933119e21f65479d0864a8d5893e494cf5daab15df0247c17/charset_normalizer-3.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:899d28f422116b08be5118ef350c292b36fc15ec2daeb9ea987c89281c7bb5c4", size = 142817, upload-time = "2026-03-15T18:51:14.408Z" }, { url = "https://files.pythonhosted.org/packages/1e/1d/4fdabeef4e231153b6ed7567602f3b68265ec4e5b76d6024cf647d43d981/charset_normalizer-3.4.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:11afb56037cbc4b1555a34dd69151e8e069bee82e613a73bef6e714ce733585f", size = 294823, upload-time = "2026-03-15T18:51:15.755Z" }, { url = "https://files.pythonhosted.org/packages/47/7b/20e809b89c69d37be748d98e84dce6820bf663cf19cf6b942c951a3e8f41/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:423fb7e748a08f854a08a222b983f4df1912b1daedce51a72bd24fe8f26a1843", size = 198527, upload-time = "2026-03-15T18:51:17.177Z" }, { url = "https://files.pythonhosted.org/packages/37/a6/4f8d27527d59c039dce6f7622593cdcd3d70a8504d87d09eb11e9fdc6062/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d73beaac5e90173ac3deb9928a74763a6d230f494e4bfb422c217a0ad8e629bf", size = 218388, upload-time = "2026-03-15T18:51:18.934Z" }, { url = "https://files.pythonhosted.org/packages/f6/9b/4770ccb3e491a9bacf1c46cc8b812214fe367c86a96353ccc6daf87b01ec/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d60377dce4511655582e300dc1e5a5f24ba0cb229005a1d5c8d0cb72bb758ab8", size = 214563, upload-time = "2026-03-15T18:51:20.374Z" }, { url = "https://files.pythonhosted.org/packages/2b/58/a199d245894b12db0b957d627516c78e055adc3a0d978bc7f65ddaf7c399/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:530e8cebeea0d76bdcf93357aa5e41336f48c3dc709ac52da2bb167c5b8271d9", size = 206587, upload-time = "2026-03-15T18:51:21.807Z" }, { url = "https://files.pythonhosted.org/packages/7e/70/3def227f1ec56f5c69dfc8392b8bd63b11a18ca8178d9211d7cc5e5e4f27/charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:a26611d9987b230566f24a0a125f17fe0de6a6aff9f25c9f564aaa2721a5fb88", size = 194724, upload-time = "2026-03-15T18:51:23.508Z" }, { url = "https://files.pythonhosted.org/packages/58/ab/9318352e220c05efd31c2779a23b50969dc94b985a2efa643ed9077bfca5/charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:34315ff4fc374b285ad7f4a0bf7dcbfe769e1b104230d40f49f700d4ab6bbd84", size = 202956, upload-time = "2026-03-15T18:51:25.239Z" }, { url = "https://files.pythonhosted.org/packages/75/13/f3550a3ac25b70f87ac98c40d3199a8503676c2f1620efbf8d42095cfc40/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ddd609f9e1af8c7bd6e2aca279c931aefecd148a14402d4e368f3171769fd", size = 201923, upload-time = "2026-03-15T18:51:26.682Z" }, { url = "https://files.pythonhosted.org/packages/1b/db/c5c643b912740b45e8eec21de1bbab8e7fc085944d37e1e709d3dcd9d72f/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:80d0a5615143c0b3225e5e3ef22c8d5d51f3f72ce0ea6fb84c943546c7b25b6c", size = 195366, upload-time = "2026-03-15T18:51:28.129Z" }, { url = "https://files.pythonhosted.org/packages/5a/67/3b1c62744f9b2448443e0eb160d8b001c849ec3fef591e012eda6484787c/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:92734d4d8d187a354a556626c221cd1a892a4e0802ccb2af432a1d85ec012194", size = 219752, upload-time = "2026-03-15T18:51:29.556Z" }, { url = "https://files.pythonhosted.org/packages/f6/98/32ffbaf7f0366ffb0445930b87d103f6b406bc2c271563644bde8a2b1093/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:613f19aa6e082cf96e17e3ffd89383343d0d589abda756b7764cf78361fd41dc", size = 203296, upload-time = "2026-03-15T18:51:30.921Z" }, { url = "https://files.pythonhosted.org/packages/41/12/5d308c1bbe60cabb0c5ef511574a647067e2a1f631bc8634fcafaccd8293/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:2b1a63e8224e401cafe7739f77efd3f9e7f5f2026bda4aead8e59afab537784f", size = 215956, upload-time = "2026-03-15T18:51:32.399Z" }, { url = "https://files.pythonhosted.org/packages/53/e9/5f85f6c5e20669dbe56b165c67b0260547dea97dba7e187938833d791687/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6cceb5473417d28edd20c6c984ab6fee6c6267d38d906823ebfe20b03d607dc2", size = 208652, upload-time = "2026-03-15T18:51:34.214Z" }, { url = "https://files.pythonhosted.org/packages/f1/11/897052ea6af56df3eef3ca94edafee410ca699ca0c7b87960ad19932c55e/charset_normalizer-3.4.6-cp313-cp313-win32.whl", hash = "sha256:d7de2637729c67d67cf87614b566626057e95c303bc0a55ffe391f5205e7003d", size = 143940, upload-time = "2026-03-15T18:51:36.15Z" }, { url = "https://files.pythonhosted.org/packages/a1/5c/724b6b363603e419829f561c854b87ed7c7e31231a7908708ac086cdf3e2/charset_normalizer-3.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:572d7c822caf521f0525ba1bce1a622a0b85cf47ffbdae6c9c19e3b5ac3c4389", size = 154101, upload-time = "2026-03-15T18:51:37.876Z" }, { url = "https://files.pythonhosted.org/packages/01/a5/7abf15b4c0968e47020f9ca0935fb3274deb87cb288cd187cad92e8cdffd/charset_normalizer-3.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a4474d924a47185a06411e0064b803c68be044be2d60e50e8bddcc2649957c1f", size = 143109, upload-time = "2026-03-15T18:51:39.565Z" }, { url = "https://files.pythonhosted.org/packages/25/6f/ffe1e1259f384594063ea1869bfb6be5cdb8bc81020fc36c3636bc8302a1/charset_normalizer-3.4.6-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:9cc6e6d9e571d2f863fa77700701dae73ed5f78881efc8b3f9a4398772ff53e8", size = 294458, upload-time = "2026-03-15T18:51:41.134Z" }, { url = "https://files.pythonhosted.org/packages/56/60/09bb6c13a8c1016c2ed5c6a6488e4ffef506461aa5161662bd7636936fb1/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5960d965e67165d75b7c7ffc60a83ec5abfc5c11b764ec13ea54fbef8b4421", size = 199277, upload-time = "2026-03-15T18:51:42.953Z" }, { url = "https://files.pythonhosted.org/packages/00/50/dcfbb72a5138bbefdc3332e8d81a23494bf67998b4b100703fd15fa52d81/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b3694e3f87f8ac7ce279d4355645b3c878d24d1424581b46282f24b92f5a4ae2", size = 218758, upload-time = "2026-03-15T18:51:44.339Z" }, { url = "https://files.pythonhosted.org/packages/03/b3/d79a9a191bb75f5aa81f3aaaa387ef29ce7cb7a9e5074ba8ea095cc073c2/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5d11595abf8dd942a77883a39d81433739b287b6aa71620f15164f8096221b30", size = 215299, upload-time = "2026-03-15T18:51:45.871Z" }, { url = "https://files.pythonhosted.org/packages/76/7e/bc8911719f7084f72fd545f647601ea3532363927f807d296a8c88a62c0d/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7bda6eebafd42133efdca535b04ccb338ab29467b3f7bf79569883676fc628db", size = 206811, upload-time = "2026-03-15T18:51:47.308Z" }, { url = "https://files.pythonhosted.org/packages/e2/40/c430b969d41dda0c465aa36cc7c2c068afb67177bef50905ac371b28ccc7/charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:bbc8c8650c6e51041ad1be191742b8b421d05bbd3410f43fa2a00c8db87678e8", size = 193706, upload-time = "2026-03-15T18:51:48.849Z" }, { url = "https://files.pythonhosted.org/packages/48/15/e35e0590af254f7df984de1323640ef375df5761f615b6225ba8deb9799a/charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:22c6f0c2fbc31e76c3b8a86fba1a56eda6166e238c29cdd3d14befdb4a4e4815", size = 202706, upload-time = "2026-03-15T18:51:50.257Z" }, { url = "https://files.pythonhosted.org/packages/5e/bd/f736f7b9cc5e93a18b794a50346bb16fbfd6b37f99e8f306f7951d27c17c/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7edbed096e4a4798710ed6bc75dcaa2a21b68b6c356553ac4823c3658d53743a", size = 202497, upload-time = "2026-03-15T18:51:52.012Z" }, { url = "https://files.pythonhosted.org/packages/9d/ba/2cc9e3e7dfdf7760a6ed8da7446d22536f3d0ce114ac63dee2a5a3599e62/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:7f9019c9cb613f084481bd6a100b12e1547cf2efe362d873c2e31e4035a6fa43", size = 193511, upload-time = "2026-03-15T18:51:53.723Z" }, { url = "https://files.pythonhosted.org/packages/9e/cb/5be49b5f776e5613be07298c80e1b02a2d900f7a7de807230595c85a8b2e/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:58c948d0d086229efc484fe2f30c2d382c86720f55cd9bc33591774348ad44e0", size = 220133, upload-time = "2026-03-15T18:51:55.333Z" }, { url = "https://files.pythonhosted.org/packages/83/43/99f1b5dad345accb322c80c7821071554f791a95ee50c1c90041c157ae99/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:419a9d91bd238052642a51938af8ac05da5b3343becde08d5cdeab9046df9ee1", size = 203035, upload-time = "2026-03-15T18:51:56.736Z" }, { url = "https://files.pythonhosted.org/packages/87/9a/62c2cb6a531483b55dddff1a68b3d891a8b498f3ca555fbcf2978e804d9d/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5273b9f0b5835ff0350c0828faea623c68bfa65b792720c453e22b25cc72930f", size = 216321, upload-time = "2026-03-15T18:51:58.17Z" }, { url = "https://files.pythonhosted.org/packages/6e/79/94a010ff81e3aec7c293eb82c28f930918e517bc144c9906a060844462eb/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:0e901eb1049fdb80f5bd11ed5ea1e498ec423102f7a9b9e4645d5b8204ff2815", size = 208973, upload-time = "2026-03-15T18:51:59.998Z" }, { url = "https://files.pythonhosted.org/packages/2a/57/4ecff6d4ec8585342f0c71bc03efaa99cb7468f7c91a57b105bcd561cea8/charset_normalizer-3.4.6-cp314-cp314-win32.whl", hash = "sha256:b4ff1d35e8c5bd078be89349b6f3a845128e685e751b6ea1169cf2160b344c4d", size = 144610, upload-time = "2026-03-15T18:52:02.213Z" }, { url = "https://files.pythonhosted.org/packages/80/94/8434a02d9d7f168c25767c64671fead8d599744a05d6a6c877144c754246/charset_normalizer-3.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:74119174722c4349af9708993118581686f343adc1c8c9c007d59be90d077f3f", size = 154962, upload-time = "2026-03-15T18:52:03.658Z" }, { url = "https://files.pythonhosted.org/packages/46/4c/48f2cdbfd923026503dfd67ccea45c94fd8fe988d9056b468579c66ed62b/charset_normalizer-3.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:e5bcc1a1ae744e0bb59641171ae53743760130600da8db48cbb6e4918e186e4e", size = 143595, upload-time = "2026-03-15T18:52:05.123Z" }, { url = "https://files.pythonhosted.org/packages/31/93/8878be7569f87b14f1d52032946131bcb6ebbd8af3e20446bc04053dc3f1/charset_normalizer-3.4.6-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ad8faf8df23f0378c6d527d8b0b15ea4a2e23c89376877c598c4870d1b2c7866", size = 314828, upload-time = "2026-03-15T18:52:06.831Z" }, { url = "https://files.pythonhosted.org/packages/06/b6/fae511ca98aac69ecc35cde828b0a3d146325dd03d99655ad38fc2cc3293/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f5ea69428fa1b49573eef0cc44a1d43bebd45ad0c611eb7d7eac760c7ae771bc", size = 208138, upload-time = "2026-03-15T18:52:08.239Z" }, { url = "https://files.pythonhosted.org/packages/54/57/64caf6e1bf07274a1e0b7c160a55ee9e8c9ec32c46846ce59b9c333f7008/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:06a7e86163334edfc5d20fe104db92fcd666e5a5df0977cb5680a506fe26cc8e", size = 224679, upload-time = "2026-03-15T18:52:10.043Z" }, { url = "https://files.pythonhosted.org/packages/aa/cb/9ff5a25b9273ef160861b41f6937f86fae18b0792fe0a8e75e06acb08f1d/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e1f6e2f00a6b8edb562826e4632e26d063ac10307e80f7461f7de3ad8ef3f077", size = 223475, upload-time = "2026-03-15T18:52:11.854Z" }, { url = "https://files.pythonhosted.org/packages/fc/97/440635fc093b8d7347502a377031f9605a1039c958f3cd18dcacffb37743/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b52c68d64c1878818687a473a10547b3292e82b6f6fe483808fb1468e2f52f", size = 215230, upload-time = "2026-03-15T18:52:13.325Z" }, { url = "https://files.pythonhosted.org/packages/cd/24/afff630feb571a13f07c8539fbb502d2ab494019492aaffc78ef41f1d1d0/charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:7504e9b7dc05f99a9bbb4525c67a2c155073b44d720470a148b34166a69c054e", size = 199045, upload-time = "2026-03-15T18:52:14.752Z" }, { url = "https://files.pythonhosted.org/packages/e5/17/d1399ecdaf7e0498c327433e7eefdd862b41236a7e484355b8e0e5ebd64b/charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:172985e4ff804a7ad08eebec0a1640ece87ba5041d565fff23c8f99c1f389484", size = 211658, upload-time = "2026-03-15T18:52:16.278Z" }, { url = "https://files.pythonhosted.org/packages/b5/38/16baa0affb957b3d880e5ac2144caf3f9d7de7bc4a91842e447fbb5e8b67/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4be9f4830ba8741527693848403e2c457c16e499100963ec711b1c6f2049b7c7", size = 210769, upload-time = "2026-03-15T18:52:17.782Z" }, { url = "https://files.pythonhosted.org/packages/05/34/c531bc6ac4c21da9ddfddb3107be2287188b3ea4b53b70fc58f2a77ac8d8/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:79090741d842f564b1b2827c0b82d846405b744d31e84f18d7a7b41c20e473ff", size = 201328, upload-time = "2026-03-15T18:52:19.553Z" }, { url = "https://files.pythonhosted.org/packages/fa/73/a5a1e9ca5f234519c1953608a03fe109c306b97fdfb25f09182babad51a7/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:87725cfb1a4f1f8c2fc9890ae2f42094120f4b44db9360be5d99a4c6b0e03a9e", size = 225302, upload-time = "2026-03-15T18:52:21.043Z" }, { url = "https://files.pythonhosted.org/packages/ba/f6/cd782923d112d296294dea4bcc7af5a7ae0f86ab79f8fefbda5526b6cfc0/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:fcce033e4021347d80ed9c66dcf1e7b1546319834b74445f561d2e2221de5659", size = 211127, upload-time = "2026-03-15T18:52:22.491Z" }, { url = "https://files.pythonhosted.org/packages/0e/c5/0b6898950627af7d6103a449b22320372c24c6feda91aa24e201a478d161/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:ca0276464d148c72defa8bb4390cce01b4a0e425f3b50d1435aa6d7a18107602", size = 222840, upload-time = "2026-03-15T18:52:24.113Z" }, { url = "https://files.pythonhosted.org/packages/7d/25/c4bba773bef442cbdc06111d40daa3de5050a676fa26e85090fc54dd12f0/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:197c1a244a274bb016dd8b79204850144ef77fe81c5b797dc389327adb552407", size = 216890, upload-time = "2026-03-15T18:52:25.541Z" }, { url = "https://files.pythonhosted.org/packages/35/1a/05dacadb0978da72ee287b0143097db12f2e7e8d3ffc4647da07a383b0b7/charset_normalizer-3.4.6-cp314-cp314t-win32.whl", hash = "sha256:2a24157fa36980478dd1770b585c0f30d19e18f4fb0c47c13aa568f871718579", size = 155379, upload-time = "2026-03-15T18:52:27.05Z" }, { url = "https://files.pythonhosted.org/packages/5d/7a/d269d834cb3a76291651256f3b9a5945e81d0a49ab9f4a498964e83c0416/charset_normalizer-3.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:cd5e2801c89992ed8c0a3f0293ae83c159a60d9a5d685005383ef4caca77f2c4", size = 169043, upload-time = "2026-03-15T18:52:28.502Z" }, { url = "https://files.pythonhosted.org/packages/23/06/28b29fba521a37a8932c6a84192175c34d49f84a6d4773fa63d05f9aff22/charset_normalizer-3.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:47955475ac79cc504ef2704b192364e51d0d473ad452caedd0002605f780101c", size = 148523, upload-time = "2026-03-15T18:52:29.956Z" }, { url = "https://files.pythonhosted.org/packages/41/85/580dbaa12ab31041ed7df59f0bebc8893514fc21da6c05c3a1c1707d118f/charset_normalizer-3.4.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:31215157227939b4fb3d740cd23fe27be0439afef67b785a1eb78a3ae69cba9e", size = 298620, upload-time = "2026-03-15T18:52:57.332Z" }, { url = "https://files.pythonhosted.org/packages/67/2c/1e55af3a5e2f52e44396d5c5b731e0ae4f3bb92915ff09a610fb2f4497eb/charset_normalizer-3.4.6-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecbbd45615a6885fe3240eb9db73b9e62518b611850fdf8ab08bd56de7ad2b17", size = 200106, upload-time = "2026-03-15T18:52:59.2Z" }, { url = "https://files.pythonhosted.org/packages/10/42/0f2f51a1d16caa45fbf384fd337d4242df1a5b313babee211381d2d39a96/charset_normalizer-3.4.6-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c45a03a4c69820a399f1dda9e1d8fbf3562eda46e7720458180302021b08f778", size = 220539, upload-time = "2026-03-15T18:53:01.019Z" }, { url = "https://files.pythonhosted.org/packages/1c/0c/4e10996c740eec0f4ae8afbbbfa25f66e8479c4b6ee9cff1ca366a4f6c04/charset_normalizer-3.4.6-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e8aeb10fcbe92767f0fa69ad5a72deca50d0dca07fbde97848997d778a50c9fe", size = 215821, upload-time = "2026-03-15T18:53:02.621Z" }, { url = "https://files.pythonhosted.org/packages/46/73/205ae7644ebb581a7c6fa9c3751e283606e145f0e6f066003c66aafc9973/charset_normalizer-3.4.6-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:54fae94be3d75f3e573c9a1b5402dc593de19377013c9a0e4285e3d402dd3a2a", size = 207917, upload-time = "2026-03-15T18:53:04.413Z" }, { url = "https://files.pythonhosted.org/packages/b3/ca/18f7dcf19afdab8097aeb2feb8b3809bb4b6ee356cb720abf5263d79406a/charset_normalizer-3.4.6-cp39-cp39-manylinux_2_31_armv7l.whl", hash = "sha256:2f7fdd9b6e6c529d6a2501a2d36b240109e78a8ceaef5687cfcfa2bbe671d297", size = 194513, upload-time = "2026-03-15T18:53:06.025Z" }, { url = "https://files.pythonhosted.org/packages/e4/6a/e7e3e204c8d79832a091e00b24595af1d5d9800d37dc1f67a6b264cc99a6/charset_normalizer-3.4.6-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4d1d02209e06550bdaef34af58e041ad71b88e624f5d825519da3a3308e22687", size = 205612, upload-time = "2026-03-15T18:53:07.494Z" }, { url = "https://files.pythonhosted.org/packages/ff/ae/2169ebcea2851c5460c7a21993a0f87028be3c3e60899cb36251e1135cf5/charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8bc5f0687d796c05b1e28ab0d38a50e6309906ee09375dd3aff6a9c09dd6e8f4", size = 203519, upload-time = "2026-03-15T18:53:09.048Z" }, { url = "https://files.pythonhosted.org/packages/43/a0/6a49a925b9c225fe35dffeac5c76f68996b814c637e9d7213718f96be109/charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:ee4ec14bc1680d6b0afab9aea2ef27e26d2024f18b24a2d7155a52b60da7e833", size = 195411, upload-time = "2026-03-15T18:53:10.542Z" }, { url = "https://files.pythonhosted.org/packages/47/f7/a26b0a18e52b1a0f11f53c2c400ed062f386ac227a64ae4be4c5a64699be/charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:d1a2ee9c1499fc8f86f4521f27a973c914b211ffa87322f4ee33bb35392da2c5", size = 221653, upload-time = "2026-03-15T18:53:12.394Z" }, { url = "https://files.pythonhosted.org/packages/a7/3a/ed1d3b5bb55e3634bd5c31cedbe4fff79d0e5b8d9a062f663a757a07760d/charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:48696db7f18afb80a068821504296eb0787d9ce239b91ca15059d1d3eaacf13b", size = 205650, upload-time = "2026-03-15T18:53:13.934Z" }, { url = "https://files.pythonhosted.org/packages/b1/27/c75819eea5ceeefc49bae329327bb91e81adc346e2a9873d9fdb9e77cde6/charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4f41da960b196ea355357285ad1316a00099f22d0929fe168343b99b254729c9", size = 216919, upload-time = "2026-03-15T18:53:15.44Z" }, { url = "https://files.pythonhosted.org/packages/0f/42/6e91bf8b15f67b7c957091138a36057a083e60703cc27848d5e36ca1eb03/charset_normalizer-3.4.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:802168e03fba8bbc5ce0d866d589e4b1ca751d06edee69f7f3a19c5a9fe6b597", size = 210101, upload-time = "2026-03-15T18:53:17.045Z" }, { url = "https://files.pythonhosted.org/packages/99/ff/101af2605e66a7ee59961d7f9e1060df7c92e8ea54208a02ab881422c24e/charset_normalizer-3.4.6-cp39-cp39-win32.whl", hash = "sha256:8761ac29b6c81574724322a554605608a9960769ea83d2c73e396f3df896ad54", size = 144136, upload-time = "2026-03-15T18:53:19.152Z" }, { url = "https://files.pythonhosted.org/packages/1d/da/de5942dfbf21f28c19e9202267dabf7bc73f195465d020a3a60054520cc5/charset_normalizer-3.4.6-cp39-cp39-win_amd64.whl", hash = "sha256:1cf0a70018692f85172348fe06d3a4b63f94ecb055e13a00c644d368eb82e5b8", size = 154210, upload-time = "2026-03-15T18:53:20.576Z" }, { url = "https://files.pythonhosted.org/packages/06/df/1b780a25b86d22b1d736f6ac883afd38ffdf30ddc18e5dc0e82211f493f1/charset_normalizer-3.4.6-cp39-cp39-win_arm64.whl", hash = "sha256:3516bbb8d42169de9e61b8520cbeeeb716f12f4ecfe3fd30a9919aa16c806ca8", size = 143225, upload-time = "2026-03-15T18:53:22.072Z" }, { url = "https://files.pythonhosted.org/packages/2a/68/687187c7e26cb24ccbd88e5069f5ef00eba804d36dde11d99aad0838ab45/charset_normalizer-3.4.6-py3-none-any.whl", hash = "sha256:947cf925bc916d90adba35a64c82aace04fa39b46b52d4630ece166655905a69", size = 61455, upload-time = "2026-03-15T18:53:23.833Z" }, ] [[package]] name = "click" version = "8.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, ] [[package]] name = "colorama" version = "0.4.6" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, ] [[package]] name = "cryptography" version = "46.0.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cffi", marker = "platform_python_implementation != 'PyPy' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "typing-extensions", marker = "python_full_version < '3.11' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/60/04/ee2a9e8542e4fa2773b81771ff8349ff19cdd56b7258a0cc442639052edb/cryptography-46.0.5.tar.gz", hash = "sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d", size = 750064, upload-time = "2026-02-10T19:18:38.255Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/f7/81/b0bb27f2ba931a65409c6b8a8b358a7f03c0e46eceacddff55f7c84b1f3b/cryptography-46.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad", size = 7176289, upload-time = "2026-02-10T19:17:08.274Z" }, { url = "https://files.pythonhosted.org/packages/ff/9e/6b4397a3e3d15123de3b1806ef342522393d50736c13b20ec4c9ea6693a6/cryptography-46.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b", size = 4275637, upload-time = "2026-02-10T19:17:10.53Z" }, { url = "https://files.pythonhosted.org/packages/63/e7/471ab61099a3920b0c77852ea3f0ea611c9702f651600397ac567848b897/cryptography-46.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b", size = 4424742, upload-time = "2026-02-10T19:17:12.388Z" }, { url = "https://files.pythonhosted.org/packages/37/53/a18500f270342d66bf7e4d9f091114e31e5ee9e7375a5aba2e85a91e0044/cryptography-46.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263", size = 4277528, upload-time = "2026-02-10T19:17:13.853Z" }, { url = "https://files.pythonhosted.org/packages/22/29/c2e812ebc38c57b40e7c583895e73c8c5adb4d1e4a0cc4c5a4fdab2b1acc/cryptography-46.0.5-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d", size = 4947993, upload-time = "2026-02-10T19:17:15.618Z" }, { url = "https://files.pythonhosted.org/packages/6b/e7/237155ae19a9023de7e30ec64e5d99a9431a567407ac21170a046d22a5a3/cryptography-46.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed", size = 4456855, upload-time = "2026-02-10T19:17:17.221Z" }, { url = "https://files.pythonhosted.org/packages/2d/87/fc628a7ad85b81206738abbd213b07702bcbdada1dd43f72236ef3cffbb5/cryptography-46.0.5-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2", size = 3984635, upload-time = "2026-02-10T19:17:18.792Z" }, { url = "https://files.pythonhosted.org/packages/84/29/65b55622bde135aedf4565dc509d99b560ee4095e56989e815f8fd2aa910/cryptography-46.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2", size = 4277038, upload-time = "2026-02-10T19:17:20.256Z" }, { url = "https://files.pythonhosted.org/packages/bc/36/45e76c68d7311432741faf1fbf7fac8a196a0a735ca21f504c75d37e2558/cryptography-46.0.5-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0", size = 4912181, upload-time = "2026-02-10T19:17:21.825Z" }, { url = "https://files.pythonhosted.org/packages/6d/1a/c1ba8fead184d6e3d5afcf03d569acac5ad063f3ac9fb7258af158f7e378/cryptography-46.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731", size = 4456482, upload-time = "2026-02-10T19:17:25.133Z" }, { url = "https://files.pythonhosted.org/packages/f9/e5/3fb22e37f66827ced3b902cf895e6a6bc1d095b5b26be26bd13c441fdf19/cryptography-46.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82", size = 4405497, upload-time = "2026-02-10T19:17:26.66Z" }, { url = "https://files.pythonhosted.org/packages/1a/df/9d58bb32b1121a8a2f27383fabae4d63080c7ca60b9b5c88be742be04ee7/cryptography-46.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1", size = 4667819, upload-time = "2026-02-10T19:17:28.569Z" }, { url = "https://files.pythonhosted.org/packages/ea/ed/325d2a490c5e94038cdb0117da9397ece1f11201f425c4e9c57fe5b9f08b/cryptography-46.0.5-cp311-abi3-win32.whl", hash = "sha256:60ee7e19e95104d4c03871d7d7dfb3d22ef8a9b9c6778c94e1c8fcc8365afd48", size = 3028230, upload-time = "2026-02-10T19:17:30.518Z" }, { url = "https://files.pythonhosted.org/packages/e9/5a/ac0f49e48063ab4255d9e3b79f5def51697fce1a95ea1370f03dc9db76f6/cryptography-46.0.5-cp311-abi3-win_amd64.whl", hash = "sha256:38946c54b16c885c72c4f59846be9743d699eee2b69b6988e0a00a01f46a61a4", size = 3480909, upload-time = "2026-02-10T19:17:32.083Z" }, { url = "https://files.pythonhosted.org/packages/00/13/3d278bfa7a15a96b9dc22db5a12ad1e48a9eb3d40e1827ef66a5df75d0d0/cryptography-46.0.5-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:94a76daa32eb78d61339aff7952ea819b1734b46f73646a07decb40e5b3448e2", size = 7119287, upload-time = "2026-02-10T19:17:33.801Z" }, { url = "https://files.pythonhosted.org/packages/67/c8/581a6702e14f0898a0848105cbefd20c058099e2c2d22ef4e476dfec75d7/cryptography-46.0.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5be7bf2fb40769e05739dd0046e7b26f9d4670badc7b032d6ce4db64dddc0678", size = 4265728, upload-time = "2026-02-10T19:17:35.569Z" }, { url = "https://files.pythonhosted.org/packages/dd/4a/ba1a65ce8fc65435e5a849558379896c957870dd64fecea97b1ad5f46a37/cryptography-46.0.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe346b143ff9685e40192a4960938545c699054ba11d4f9029f94751e3f71d87", size = 4408287, upload-time = "2026-02-10T19:17:36.938Z" }, { url = "https://files.pythonhosted.org/packages/f8/67/8ffdbf7b65ed1ac224d1c2df3943553766914a8ca718747ee3871da6107e/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c69fd885df7d089548a42d5ec05be26050ebcd2283d89b3d30676eb32ff87dee", size = 4270291, upload-time = "2026-02-10T19:17:38.748Z" }, { url = "https://files.pythonhosted.org/packages/f8/e5/f52377ee93bc2f2bba55a41a886fd208c15276ffbd2569f2ddc89d50e2c5/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:8293f3dea7fc929ef7240796ba231413afa7b68ce38fd21da2995549f5961981", size = 4927539, upload-time = "2026-02-10T19:17:40.241Z" }, { url = "https://files.pythonhosted.org/packages/3b/02/cfe39181b02419bbbbcf3abdd16c1c5c8541f03ca8bda240debc467d5a12/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:1abfdb89b41c3be0365328a410baa9df3ff8a9110fb75e7b52e66803ddabc9a9", size = 4442199, upload-time = "2026-02-10T19:17:41.789Z" }, { url = "https://files.pythonhosted.org/packages/c0/96/2fcaeb4873e536cf71421a388a6c11b5bc846e986b2b069c79363dc1648e/cryptography-46.0.5-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:d66e421495fdb797610a08f43b05269e0a5ea7f5e652a89bfd5a7d3c1dee3648", size = 3960131, upload-time = "2026-02-10T19:17:43.379Z" }, { url = "https://files.pythonhosted.org/packages/d8/d2/b27631f401ddd644e94c5cf33c9a4069f72011821cf3dc7309546b0642a0/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:4e817a8920bfbcff8940ecfd60f23d01836408242b30f1a708d93198393a80b4", size = 4270072, upload-time = "2026-02-10T19:17:45.481Z" }, { url = "https://files.pythonhosted.org/packages/f4/a7/60d32b0370dae0b4ebe55ffa10e8599a2a59935b5ece1b9f06edb73abdeb/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:68f68d13f2e1cb95163fa3b4db4bf9a159a418f5f6e7242564fc75fcae667fd0", size = 4892170, upload-time = "2026-02-10T19:17:46.997Z" }, { url = "https://files.pythonhosted.org/packages/d2/b9/cf73ddf8ef1164330eb0b199a589103c363afa0cf794218c24d524a58eab/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a3d1fae9863299076f05cb8a778c467578262fae09f9dc0ee9b12eb4268ce663", size = 4441741, upload-time = "2026-02-10T19:17:48.661Z" }, { url = "https://files.pythonhosted.org/packages/5f/eb/eee00b28c84c726fe8fa0158c65afe312d9c3b78d9d01daf700f1f6e37ff/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4143987a42a2397f2fc3b4d7e3a7d313fbe684f67ff443999e803dd75a76826", size = 4396728, upload-time = "2026-02-10T19:17:50.058Z" }, { url = "https://files.pythonhosted.org/packages/65/f4/6bc1a9ed5aef7145045114b75b77c2a8261b4d38717bd8dea111a63c3442/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7d731d4b107030987fd61a7f8ab512b25b53cef8f233a97379ede116f30eb67d", size = 4652001, upload-time = "2026-02-10T19:17:51.54Z" }, { url = "https://files.pythonhosted.org/packages/86/ef/5d00ef966ddd71ac2e6951d278884a84a40ffbd88948ef0e294b214ae9e4/cryptography-46.0.5-cp314-cp314t-win32.whl", hash = "sha256:c3bcce8521d785d510b2aad26ae2c966092b7daa8f45dd8f44734a104dc0bc1a", size = 3003637, upload-time = "2026-02-10T19:17:52.997Z" }, { url = "https://files.pythonhosted.org/packages/b7/57/f3f4160123da6d098db78350fdfd9705057aad21de7388eacb2401dceab9/cryptography-46.0.5-cp314-cp314t-win_amd64.whl", hash = "sha256:4d8ae8659ab18c65ced284993c2265910f6c9e650189d4e3f68445ef82a810e4", size = 3469487, upload-time = "2026-02-10T19:17:54.549Z" }, { url = "https://files.pythonhosted.org/packages/e2/fa/a66aa722105ad6a458bebd64086ca2b72cdd361fed31763d20390f6f1389/cryptography-46.0.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31", size = 7170514, upload-time = "2026-02-10T19:17:56.267Z" }, { url = "https://files.pythonhosted.org/packages/0f/04/c85bdeab78c8bc77b701bf0d9bdcf514c044e18a46dcff330df5448631b0/cryptography-46.0.5-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18", size = 4275349, upload-time = "2026-02-10T19:17:58.419Z" }, { url = "https://files.pythonhosted.org/packages/5c/32/9b87132a2f91ee7f5223b091dc963055503e9b442c98fc0b8a5ca765fab0/cryptography-46.0.5-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235", size = 4420667, upload-time = "2026-02-10T19:18:00.619Z" }, { url = "https://files.pythonhosted.org/packages/a1/a6/a7cb7010bec4b7c5692ca6f024150371b295ee1c108bdc1c400e4c44562b/cryptography-46.0.5-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a", size = 4276980, upload-time = "2026-02-10T19:18:02.379Z" }, { url = "https://files.pythonhosted.org/packages/8e/7c/c4f45e0eeff9b91e3f12dbd0e165fcf2a38847288fcfd889deea99fb7b6d/cryptography-46.0.5-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76", size = 4939143, upload-time = "2026-02-10T19:18:03.964Z" }, { url = "https://files.pythonhosted.org/packages/37/19/e1b8f964a834eddb44fa1b9a9976f4e414cbb7aa62809b6760c8803d22d1/cryptography-46.0.5-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614", size = 4453674, upload-time = "2026-02-10T19:18:05.588Z" }, { url = "https://files.pythonhosted.org/packages/db/ed/db15d3956f65264ca204625597c410d420e26530c4e2943e05a0d2f24d51/cryptography-46.0.5-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229", size = 3978801, upload-time = "2026-02-10T19:18:07.167Z" }, { url = "https://files.pythonhosted.org/packages/41/e2/df40a31d82df0a70a0daf69791f91dbb70e47644c58581d654879b382d11/cryptography-46.0.5-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1", size = 4276755, upload-time = "2026-02-10T19:18:09.813Z" }, { url = "https://files.pythonhosted.org/packages/33/45/726809d1176959f4a896b86907b98ff4391a8aa29c0aaaf9450a8a10630e/cryptography-46.0.5-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d", size = 4901539, upload-time = "2026-02-10T19:18:11.263Z" }, { url = "https://files.pythonhosted.org/packages/99/0f/a3076874e9c88ecb2ecc31382f6e7c21b428ede6f55aafa1aa272613e3cd/cryptography-46.0.5-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c", size = 4452794, upload-time = "2026-02-10T19:18:12.914Z" }, { url = "https://files.pythonhosted.org/packages/02/ef/ffeb542d3683d24194a38f66ca17c0a4b8bf10631feef44a7ef64e631b1a/cryptography-46.0.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4", size = 4404160, upload-time = "2026-02-10T19:18:14.375Z" }, { url = "https://files.pythonhosted.org/packages/96/93/682d2b43c1d5f1406ed048f377c0fc9fc8f7b0447a478d5c65ab3d3a66eb/cryptography-46.0.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9", size = 4667123, upload-time = "2026-02-10T19:18:15.886Z" }, { url = "https://files.pythonhosted.org/packages/45/2d/9c5f2926cb5300a8eefc3f4f0b3f3df39db7f7ce40c8365444c49363cbda/cryptography-46.0.5-cp38-abi3-win32.whl", hash = "sha256:02f547fce831f5096c9a567fd41bc12ca8f11df260959ecc7c3202555cc47a72", size = 3010220, upload-time = "2026-02-10T19:18:17.361Z" }, { url = "https://files.pythonhosted.org/packages/48/ef/0c2f4a8e31018a986949d34a01115dd057bf536905dca38897bacd21fac3/cryptography-46.0.5-cp38-abi3-win_amd64.whl", hash = "sha256:556e106ee01aa13484ce9b0239bca667be5004efb0aabbed28d353df86445595", size = 3467050, upload-time = "2026-02-10T19:18:18.899Z" }, { url = "https://files.pythonhosted.org/packages/eb/dd/2d9fdb07cebdf3d51179730afb7d5e576153c6744c3ff8fded23030c204e/cryptography-46.0.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:3b4995dc971c9fb83c25aa44cf45f02ba86f71ee600d81091c2f0cbae116b06c", size = 3476964, upload-time = "2026-02-10T19:18:20.687Z" }, { url = "https://files.pythonhosted.org/packages/e9/6f/6cc6cc9955caa6eaf83660b0da2b077c7fe8ff9950a3c5e45d605038d439/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:bc84e875994c3b445871ea7181d424588171efec3e185dced958dad9e001950a", size = 4218321, upload-time = "2026-02-10T19:18:22.349Z" }, { url = "https://files.pythonhosted.org/packages/3e/5d/c4da701939eeee699566a6c1367427ab91a8b7088cc2328c09dbee940415/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:2ae6971afd6246710480e3f15824ed3029a60fc16991db250034efd0b9fb4356", size = 4381786, upload-time = "2026-02-10T19:18:24.529Z" }, { url = "https://files.pythonhosted.org/packages/ac/97/a538654732974a94ff96c1db621fa464f455c02d4bb7d2652f4edc21d600/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:d861ee9e76ace6cf36a6a89b959ec08e7bc2493ee39d07ffe5acb23ef46d27da", size = 4217990, upload-time = "2026-02-10T19:18:25.957Z" }, { url = "https://files.pythonhosted.org/packages/ae/11/7e500d2dd3ba891197b9efd2da5454b74336d64a7cc419aa7327ab74e5f6/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:2b7a67c9cd56372f3249b39699f2ad479f6991e62ea15800973b956f4b73e257", size = 4381252, upload-time = "2026-02-10T19:18:27.496Z" }, { url = "https://files.pythonhosted.org/packages/bc/58/6b3d24e6b9bc474a2dcdee65dfd1f008867015408a271562e4b690561a4d/cryptography-46.0.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8456928655f856c6e1533ff59d5be76578a7157224dbd9ce6872f25055ab9ab7", size = 3407605, upload-time = "2026-02-10T19:18:29.233Z" }, ] [[package]] name = "deprecated" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "wrapt" }, ] sdist = { url = "https://files.pythonhosted.org/packages/49/85/12f0a49a7c4ffb70572b6c2ef13c90c88fd190debda93b23f026b25f9634/deprecated-1.3.1.tar.gz", hash = "sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223", size = 2932523, upload-time = "2025-10-30T08:19:02.757Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/84/d0/205d54408c08b13550c733c4b85429e7ead111c7f0014309637425520a9a/deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f", size = 11298, upload-time = "2025-10-30T08:19:00.758Z" }, ] [[package]] name = "dirty-equals" version = "0.11" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/30/1d/c5913ac9d6615515a00f4bdc71356d302437cb74ff2e9aaccd3c14493b78/dirty_equals-0.11.tar.gz", hash = "sha256:f4ac74ee88f2d11e2fa0f65eb30ee4f07105c5f86f4dc92b09eb1138775027c3", size = 128067, upload-time = "2025-11-17T01:51:24.451Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/bb/8d/dbff05239043271dbeace563a7686212a3dd517864a35623fe4d4a64ca19/dirty_equals-0.11-py3-none-any.whl", hash = "sha256:b1d7093273fc2f9be12f443a8ead954ef6daaf6746fd42ef3a5616433ee85286", size = 28051, upload-time = "2025-11-17T01:51:22.849Z" }, ] [[package]] name = "distro" version = "1.9.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, ] [[package]] name = "docstring-parser" version = "0.17.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/b2/9d/c3b43da9515bd270df0f80548d9944e389870713cc1fe2b8fb35fe2bcefd/docstring_parser-0.17.0.tar.gz", hash = "sha256:583de4a309722b3315439bb31d64ba3eebada841f2e2cee23b99df001434c912", size = 27442, upload-time = "2025-07-21T07:35:01.868Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl", hash = "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708", size = 36896, upload-time = "2025-07-21T07:35:00.684Z" }, ] [[package]] name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions", marker = "python_full_version < '3.13' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] 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 = [ { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, ] [[package]] name = "execnet" version = "2.1.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/bf/89/780e11f9588d9e7128a3f87788354c7946a9cbb1401ad38a48c4db9a4f07/execnet-2.1.2.tar.gz", hash = "sha256:63d83bfdd9a23e35b9c6a3261412324f964c2ec8dcd8d3c6916ee9373e0befcd", size = 166622, upload-time = "2025-11-12T09:56:37.75Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/ab/84/02fc1827e8cdded4aa65baef11296a9bbe595c474f0d6d758af082d849fd/execnet-2.1.2-py3-none-any.whl", hash = "sha256:67fba928dd5a544b783f6056f449e5e3931a5c378b128bc18501f7ea79e296ec", size = 40708, upload-time = "2025-11-12T09:56:36.333Z" }, ] [[package]] name = "executing" version = "2.2.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, ] [[package]] name = "frozenlist" version = "1.8.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/83/4a/557715d5047da48d54e659203b9335be7bfaafda2c3f627b7c47e0b3aaf3/frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011", size = 86230, upload-time = "2025-10-06T05:35:23.699Z" }, { url = "https://files.pythonhosted.org/packages/a2/fb/c85f9fed3ea8fe8740e5b46a59cc141c23b842eca617da8876cfce5f760e/frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565", size = 49621, upload-time = "2025-10-06T05:35:25.341Z" }, { url = "https://files.pythonhosted.org/packages/63/70/26ca3f06aace16f2352796b08704338d74b6d1a24ca38f2771afbb7ed915/frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad", size = 49889, upload-time = "2025-10-06T05:35:26.797Z" }, { url = "https://files.pythonhosted.org/packages/5d/ed/c7895fd2fde7f3ee70d248175f9b6cdf792fb741ab92dc59cd9ef3bd241b/frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2", size = 219464, upload-time = "2025-10-06T05:35:28.254Z" }, { url = "https://files.pythonhosted.org/packages/6b/83/4d587dccbfca74cb8b810472392ad62bfa100bf8108c7223eb4c4fa2f7b3/frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186", size = 221649, upload-time = "2025-10-06T05:35:29.454Z" }, { url = "https://files.pythonhosted.org/packages/6a/c6/fd3b9cd046ec5fff9dab66831083bc2077006a874a2d3d9247dea93ddf7e/frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e", size = 219188, upload-time = "2025-10-06T05:35:30.951Z" }, { url = "https://files.pythonhosted.org/packages/ce/80/6693f55eb2e085fc8afb28cf611448fb5b90e98e068fa1d1b8d8e66e5c7d/frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450", size = 231748, upload-time = "2025-10-06T05:35:32.101Z" }, { url = "https://files.pythonhosted.org/packages/97/d6/e9459f7c5183854abd989ba384fe0cc1a0fb795a83c033f0571ec5933ca4/frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef", size = 236351, upload-time = "2025-10-06T05:35:33.834Z" }, { url = "https://files.pythonhosted.org/packages/97/92/24e97474b65c0262e9ecd076e826bfd1d3074adcc165a256e42e7b8a7249/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4", size = 218767, upload-time = "2025-10-06T05:35:35.205Z" }, { url = "https://files.pythonhosted.org/packages/ee/bf/dc394a097508f15abff383c5108cb8ad880d1f64a725ed3b90d5c2fbf0bb/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff", size = 235887, upload-time = "2025-10-06T05:35:36.354Z" }, { url = "https://files.pythonhosted.org/packages/40/90/25b201b9c015dbc999a5baf475a257010471a1fa8c200c843fd4abbee725/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c", size = 228785, upload-time = "2025-10-06T05:35:37.949Z" }, { url = "https://files.pythonhosted.org/packages/84/f4/b5bc148df03082f05d2dd30c089e269acdbe251ac9a9cf4e727b2dbb8a3d/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f", size = 230312, upload-time = "2025-10-06T05:35:39.178Z" }, { url = "https://files.pythonhosted.org/packages/db/4b/87e95b5d15097c302430e647136b7d7ab2398a702390cf4c8601975709e7/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7", size = 217650, upload-time = "2025-10-06T05:35:40.377Z" }, { url = "https://files.pythonhosted.org/packages/e5/70/78a0315d1fea97120591a83e0acd644da638c872f142fd72a6cebee825f3/frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a", size = 39659, upload-time = "2025-10-06T05:35:41.863Z" }, { url = "https://files.pythonhosted.org/packages/66/aa/3f04523fb189a00e147e60c5b2205126118f216b0aa908035c45336e27e4/frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6", size = 43837, upload-time = "2025-10-06T05:35:43.205Z" }, { url = "https://files.pythonhosted.org/packages/39/75/1135feecdd7c336938bd55b4dc3b0dfc46d85b9be12ef2628574b28de776/frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e", size = 39989, upload-time = "2025-10-06T05:35:44.596Z" }, { url = "https://files.pythonhosted.org/packages/bc/03/077f869d540370db12165c0aa51640a873fb661d8b315d1d4d67b284d7ac/frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84", size = 86912, upload-time = "2025-10-06T05:35:45.98Z" }, { url = "https://files.pythonhosted.org/packages/df/b5/7610b6bd13e4ae77b96ba85abea1c8cb249683217ef09ac9e0ae93f25a91/frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9", size = 50046, upload-time = "2025-10-06T05:35:47.009Z" }, { url = "https://files.pythonhosted.org/packages/6e/ef/0e8f1fe32f8a53dd26bdd1f9347efe0778b0fddf62789ea683f4cc7d787d/frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93", size = 50119, upload-time = "2025-10-06T05:35:48.38Z" }, { url = "https://files.pythonhosted.org/packages/11/b1/71a477adc7c36e5fb628245dfbdea2166feae310757dea848d02bd0689fd/frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f", size = 231067, upload-time = "2025-10-06T05:35:49.97Z" }, { url = "https://files.pythonhosted.org/packages/45/7e/afe40eca3a2dc19b9904c0f5d7edfe82b5304cb831391edec0ac04af94c2/frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695", size = 233160, upload-time = "2025-10-06T05:35:51.729Z" }, { url = "https://files.pythonhosted.org/packages/a6/aa/7416eac95603ce428679d273255ffc7c998d4132cfae200103f164b108aa/frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52", size = 228544, upload-time = "2025-10-06T05:35:53.246Z" }, { url = "https://files.pythonhosted.org/packages/8b/3d/2a2d1f683d55ac7e3875e4263d28410063e738384d3adc294f5ff3d7105e/frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581", size = 243797, upload-time = "2025-10-06T05:35:54.497Z" }, { url = "https://files.pythonhosted.org/packages/78/1e/2d5565b589e580c296d3bb54da08d206e797d941a83a6fdea42af23be79c/frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567", size = 247923, upload-time = "2025-10-06T05:35:55.861Z" }, { url = "https://files.pythonhosted.org/packages/aa/c3/65872fcf1d326a7f101ad4d86285c403c87be7d832b7470b77f6d2ed5ddc/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b", size = 230886, upload-time = "2025-10-06T05:35:57.399Z" }, { url = "https://files.pythonhosted.org/packages/a0/76/ac9ced601d62f6956f03cc794f9e04c81719509f85255abf96e2510f4265/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92", size = 245731, upload-time = "2025-10-06T05:35:58.563Z" }, { url = "https://files.pythonhosted.org/packages/b9/49/ecccb5f2598daf0b4a1415497eba4c33c1e8ce07495eb07d2860c731b8d5/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d", size = 241544, upload-time = "2025-10-06T05:35:59.719Z" }, { url = "https://files.pythonhosted.org/packages/53/4b/ddf24113323c0bbcc54cb38c8b8916f1da7165e07b8e24a717b4a12cbf10/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd", size = 241806, upload-time = "2025-10-06T05:36:00.959Z" }, { url = "https://files.pythonhosted.org/packages/a7/fb/9b9a084d73c67175484ba2789a59f8eebebd0827d186a8102005ce41e1ba/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967", size = 229382, upload-time = "2025-10-06T05:36:02.22Z" }, { url = "https://files.pythonhosted.org/packages/95/a3/c8fb25aac55bf5e12dae5c5aa6a98f85d436c1dc658f21c3ac73f9fa95e5/frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25", size = 39647, upload-time = "2025-10-06T05:36:03.409Z" }, { url = "https://files.pythonhosted.org/packages/0a/f5/603d0d6a02cfd4c8f2a095a54672b3cf967ad688a60fb9faf04fc4887f65/frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b", size = 44064, upload-time = "2025-10-06T05:36:04.368Z" }, { url = "https://files.pythonhosted.org/packages/5d/16/c2c9ab44e181f043a86f9a8f84d5124b62dbcb3a02c0977ec72b9ac1d3e0/frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a", size = 39937, upload-time = "2025-10-06T05:36:05.669Z" }, { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" }, { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" }, { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" }, { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" }, { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" }, { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" }, { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" }, { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" }, { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" }, { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" }, { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" }, { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" }, { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" }, { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985, upload-time = "2025-10-06T05:36:23.661Z" }, { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591, upload-time = "2025-10-06T05:36:24.958Z" }, { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102, upload-time = "2025-10-06T05:36:26.333Z" }, { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717, upload-time = "2025-10-06T05:36:27.341Z" }, { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651, upload-time = "2025-10-06T05:36:28.855Z" }, { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417, upload-time = "2025-10-06T05:36:29.877Z" }, { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391, upload-time = "2025-10-06T05:36:31.301Z" }, { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048, upload-time = "2025-10-06T05:36:32.531Z" }, { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549, upload-time = "2025-10-06T05:36:33.706Z" }, { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833, upload-time = "2025-10-06T05:36:34.947Z" }, { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363, upload-time = "2025-10-06T05:36:36.534Z" }, { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314, upload-time = "2025-10-06T05:36:38.582Z" }, { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365, upload-time = "2025-10-06T05:36:40.152Z" }, { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763, upload-time = "2025-10-06T05:36:41.355Z" }, { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110, upload-time = "2025-10-06T05:36:42.716Z" }, { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717, upload-time = "2025-10-06T05:36:44.251Z" }, { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628, upload-time = "2025-10-06T05:36:45.423Z" }, { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882, upload-time = "2025-10-06T05:36:46.796Z" }, { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676, upload-time = "2025-10-06T05:36:47.8Z" }, { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235, upload-time = "2025-10-06T05:36:48.78Z" }, { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742, upload-time = "2025-10-06T05:36:49.837Z" }, { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725, upload-time = "2025-10-06T05:36:50.851Z" }, { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533, upload-time = "2025-10-06T05:36:51.898Z" }, { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506, upload-time = "2025-10-06T05:36:53.101Z" }, { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161, upload-time = "2025-10-06T05:36:54.309Z" }, { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676, upload-time = "2025-10-06T05:36:55.566Z" }, { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638, upload-time = "2025-10-06T05:36:56.758Z" }, { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067, upload-time = "2025-10-06T05:36:57.965Z" }, { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101, upload-time = "2025-10-06T05:36:59.237Z" }, { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901, upload-time = "2025-10-06T05:37:00.811Z" }, { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395, upload-time = "2025-10-06T05:37:02.115Z" }, { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659, upload-time = "2025-10-06T05:37:03.711Z" }, { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492, upload-time = "2025-10-06T05:37:04.915Z" }, { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034, upload-time = "2025-10-06T05:37:06.343Z" }, { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749, upload-time = "2025-10-06T05:37:07.431Z" }, { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127, upload-time = "2025-10-06T05:37:08.438Z" }, { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698, upload-time = "2025-10-06T05:37:09.48Z" }, { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749, upload-time = "2025-10-06T05:37:10.569Z" }, { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298, upload-time = "2025-10-06T05:37:11.993Z" }, { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015, upload-time = "2025-10-06T05:37:13.194Z" }, { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038, upload-time = "2025-10-06T05:37:14.577Z" }, { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130, upload-time = "2025-10-06T05:37:15.781Z" }, { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845, upload-time = "2025-10-06T05:37:17.037Z" }, { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131, upload-time = "2025-10-06T05:37:18.221Z" }, { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542, upload-time = "2025-10-06T05:37:19.771Z" }, { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308, upload-time = "2025-10-06T05:37:20.969Z" }, { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210, upload-time = "2025-10-06T05:37:22.252Z" }, { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972, upload-time = "2025-10-06T05:37:23.5Z" }, { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536, upload-time = "2025-10-06T05:37:25.581Z" }, { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330, upload-time = "2025-10-06T05:37:26.928Z" }, { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627, upload-time = "2025-10-06T05:37:28.075Z" }, { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238, upload-time = "2025-10-06T05:37:29.373Z" }, { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738, upload-time = "2025-10-06T05:37:30.792Z" }, { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739, upload-time = "2025-10-06T05:37:32.127Z" }, { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186, upload-time = "2025-10-06T05:37:33.21Z" }, { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196, upload-time = "2025-10-06T05:37:36.107Z" }, { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830, upload-time = "2025-10-06T05:37:37.663Z" }, { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289, upload-time = "2025-10-06T05:37:39.261Z" }, { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318, upload-time = "2025-10-06T05:37:43.213Z" }, { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814, upload-time = "2025-10-06T05:37:45.337Z" }, { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762, upload-time = "2025-10-06T05:37:46.657Z" }, { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470, upload-time = "2025-10-06T05:37:47.946Z" }, { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042, upload-time = "2025-10-06T05:37:49.499Z" }, { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148, upload-time = "2025-10-06T05:37:50.745Z" }, { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" }, { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" }, { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" }, { url = "https://files.pythonhosted.org/packages/c2/59/ae5cdac87a00962122ea37bb346d41b66aec05f9ce328fa2b9e216f8967b/frozenlist-1.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d8b7138e5cd0647e4523d6685b0eac5d4be9a184ae9634492f25c6eb38c12a47", size = 86967, upload-time = "2025-10-06T05:37:55.607Z" }, { url = "https://files.pythonhosted.org/packages/8a/10/17059b2db5a032fd9323c41c39e9d1f5f9d0c8f04d1e4e3e788573086e61/frozenlist-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a6483e309ca809f1efd154b4d37dc6d9f61037d6c6a81c2dc7a15cb22c8c5dca", size = 49984, upload-time = "2025-10-06T05:37:57.049Z" }, { url = "https://files.pythonhosted.org/packages/4b/de/ad9d82ca8e5fa8f0c636e64606553c79e2b859ad253030b62a21fe9986f5/frozenlist-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1b9290cf81e95e93fdf90548ce9d3c1211cf574b8e3f4b3b7cb0537cf2227068", size = 50240, upload-time = "2025-10-06T05:37:58.145Z" }, { url = "https://files.pythonhosted.org/packages/4e/45/3dfb7767c2a67d123650122b62ce13c731b6c745bc14424eea67678b508c/frozenlist-1.8.0-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:59a6a5876ca59d1b63af8cd5e7ffffb024c3dc1e9cf9301b21a2e76286505c95", size = 219472, upload-time = "2025-10-06T05:37:59.239Z" }, { url = "https://files.pythonhosted.org/packages/0b/bf/5bf23d913a741b960d5c1dac7c1985d8a2a1d015772b2d18ea168b08e7ff/frozenlist-1.8.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6dc4126390929823e2d2d9dc79ab4046ed74680360fc5f38b585c12c66cdf459", size = 221531, upload-time = "2025-10-06T05:38:00.521Z" }, { url = "https://files.pythonhosted.org/packages/d0/03/27ec393f3b55860859f4b74cdc8c2a4af3dbf3533305e8eacf48a4fd9a54/frozenlist-1.8.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:332db6b2563333c5671fecacd085141b5800cb866be16d5e3eb15a2086476675", size = 219211, upload-time = "2025-10-06T05:38:01.842Z" }, { url = "https://files.pythonhosted.org/packages/3a/ad/0fd00c404fa73fe9b169429e9a972d5ed807973c40ab6b3cf9365a33d360/frozenlist-1.8.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9ff15928d62a0b80bb875655c39bf517938c7d589554cbd2669be42d97c2cb61", size = 231775, upload-time = "2025-10-06T05:38:03.384Z" }, { url = "https://files.pythonhosted.org/packages/8a/c3/86962566154cb4d2995358bc8331bfc4ea19d07db1a96f64935a1607f2b6/frozenlist-1.8.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7bf6cdf8e07c8151fba6fe85735441240ec7f619f935a5205953d58009aef8c6", size = 236631, upload-time = "2025-10-06T05:38:04.609Z" }, { url = "https://files.pythonhosted.org/packages/ea/9e/6ffad161dbd83782d2c66dc4d378a9103b31770cb1e67febf43aea42d202/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:48e6d3f4ec5c7273dfe83ff27c91083c6c9065af655dc2684d2c200c94308bb5", size = 218632, upload-time = "2025-10-06T05:38:05.917Z" }, { url = "https://files.pythonhosted.org/packages/58/b2/4677eee46e0a97f9b30735e6ad0bf6aba3e497986066eb68807ac85cf60f/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:1a7607e17ad33361677adcd1443edf6f5da0ce5e5377b798fba20fae194825f3", size = 235967, upload-time = "2025-10-06T05:38:07.614Z" }, { url = "https://files.pythonhosted.org/packages/05/f3/86e75f8639c5a93745ca7addbbc9de6af56aebb930d233512b17e46f6493/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3a935c3a4e89c733303a2d5a7c257ea44af3a56c8202df486b7f5de40f37e1", size = 228799, upload-time = "2025-10-06T05:38:08.845Z" }, { url = "https://files.pythonhosted.org/packages/30/00/39aad3a7f0d98f5eb1d99a3c311215674ed87061aecee7851974b335c050/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:940d4a017dbfed9daf46a3b086e1d2167e7012ee297fef9e1c545c4d022f5178", size = 230566, upload-time = "2025-10-06T05:38:10.52Z" }, { url = "https://files.pythonhosted.org/packages/0d/4d/aa144cac44568d137846ddc4d5210fb5d9719eb1d7ec6fa2728a54b5b94a/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b9be22a69a014bc47e78072d0ecae716f5eb56c15238acca0f43d6eb8e4a5bda", size = 217715, upload-time = "2025-10-06T05:38:11.832Z" }, { url = "https://files.pythonhosted.org/packages/64/4c/8f665921667509d25a0dd72540513bc86b356c95541686f6442a3283019f/frozenlist-1.8.0-cp39-cp39-win32.whl", hash = "sha256:1aa77cb5697069af47472e39612976ed05343ff2e84a3dcf15437b232cbfd087", size = 39933, upload-time = "2025-10-06T05:38:13.061Z" }, { url = "https://files.pythonhosted.org/packages/79/bd/bcc926f87027fad5e59926ff12d136e1082a115025d33c032d1cd69ab377/frozenlist-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:7398c222d1d405e796970320036b1b563892b65809d9e5261487bb2c7f7b5c6a", size = 44121, upload-time = "2025-10-06T05:38:14.572Z" }, { url = "https://files.pythonhosted.org/packages/4c/07/9c2e4eb7584af4b705237b971b89a4155a8e57599c4483a131a39256a9a0/frozenlist-1.8.0-cp39-cp39-win_arm64.whl", hash = "sha256:b4f3b365f31c6cd4af24545ca0a244a53688cad8834e32f56831c4923b50a103", size = 40312, upload-time = "2025-10-06T05:38:15.699Z" }, { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, ] [[package]] name = "google-auth" version = "2.49.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cryptography" }, { name = "pyasn1-modules" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ea/80/6a696a07d3d3b0a92488933532f03dbefa4a24ab80fb231395b9a2a1be77/google_auth-2.49.1.tar.gz", hash = "sha256:16d40da1c3c5a0533f57d268fe72e0ebb0ae1cc3b567024122651c045d879b64", size = 333825, upload-time = "2026-03-12T19:30:58.135Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/e9/eb/c6c2478d8a8d633460be40e2a8a6f8f429171997a35a96f81d3b680dec83/google_auth-2.49.1-py3-none-any.whl", hash = "sha256:195ebe3dca18eddd1b3db5edc5189b76c13e96f29e73043b923ebcf3f1a860f7", size = 240737, upload-time = "2026-03-12T19:30:53.159Z" }, ] [package.optional-dependencies] requests = [ { name = "requests" }, ] [[package]] name = "griffe" version = "1.14.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ { name = "colorama", marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ec/d7/6c09dd7ce4c7837e4cdb11dce980cb45ae3cd87677298dc3b781b6bce7d3/griffe-1.14.0.tar.gz", hash = "sha256:9d2a15c1eca966d68e00517de5d69dd1bc5c9f2335ef6c1775362ba5b8651a13", size = 424684, upload-time = "2025-09-05T15:02:29.167Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/2a/b1/9ff6578d789a89812ff21e4e0f80ffae20a65d5dd84e7a17873fe3b365be/griffe-1.14.0-py3-none-any.whl", hash = "sha256:0e9d52832cccf0f7188cfe585ba962d2674b241c01916d780925df34873bceb0", size = 144439, upload-time = "2025-09-05T15:02:27.511Z" }, ] [[package]] name = "griffe" version = "2.0.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.14' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and python_full_version < '3.14' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and python_full_version < '3.14' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra != 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", ] dependencies = [ { name = "griffecli", marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "griffelib", marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/04/56/28a0accac339c164b52a92c6cfc45a903acc0c174caa5c1713803467b533/griffe-2.0.0.tar.gz", hash = "sha256:c68979cd8395422083a51ea7cf02f9c119d889646d99b7b656ee43725de1b80f", size = 293906, upload-time = "2026-03-23T21:06:53.402Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/8b/94/ee21d41e7eb4f823b94603b9d40f86d3c7fde80eacc2c3c71845476dddaa/griffe-2.0.0-py3-none-any.whl", hash = "sha256:5418081135a391c3e6e757a7f3f156f1a1a746cc7b4023868ff7d5e2f9a980aa", size = 5214, upload-time = "2026-02-09T19:09:44.105Z" }, ] [[package]] name = "griffecli" version = "2.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "griffelib", marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a4/f8/2e129fd4a86e52e58eefe664de05e7d502decf766e7316cc9e70fdec3e18/griffecli-2.0.0.tar.gz", hash = "sha256:312fa5ebb4ce6afc786356e2d0ce85b06c1c20d45abc42d74f0cda65e159f6ef", size = 56213, upload-time = "2026-03-23T21:06:54.8Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/e6/ed/d93f7a447bbf7a935d8868e9617cbe1cadf9ee9ee6bd275d3040fbf93d60/griffecli-2.0.0-py3-none-any.whl", hash = "sha256:9f7cd9ee9b21d55e91689358978d2385ae65c22f307a63fb3269acf3f21e643d", size = 9345, upload-time = "2026-02-09T19:09:42.554Z" }, ] [[package]] name = "griffelib" version = "2.0.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/ad/06/eccbd311c9e2b3ca45dbc063b93134c57a1ccc7607c5e545264ad092c4a9/griffelib-2.0.0.tar.gz", hash = "sha256:e504d637a089f5cab9b5daf18f7645970509bf4f53eda8d79ed71cce8bd97934", size = 166312, upload-time = "2026-03-23T21:06:55.954Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/4d/51/c936033e16d12b627ea334aaaaf42229c37620d0f15593456ab69ab48161/griffelib-2.0.0-py3-none-any.whl", hash = "sha256:01284878c966508b6d6f1dbff9b6fa607bc062d8261c5c7253cb285b06422a7f", size = 142004, upload-time = "2026-02-09T19:09:40.561Z" }, ] [[package]] name = "h11" version = "0.16.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, ] [[package]] name = "http-snapshot" version = "0.1.9" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "inline-snapshot" }, ] sdist = { url = "https://files.pythonhosted.org/packages/30/6d/10494f06ba67f9d74f98b25f9db3ae339969b6049e30144d7f161a1b33ac/http_snapshot-0.1.9.tar.gz", hash = "sha256:22e6a1f0ff2836e14c33724213422d6351fe9513aafbbc5e0608cad9a2c5363c", size = 11155, upload-time = "2026-02-24T21:11:28.89Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/8a/be/2a84ec7f2ac134e7628235768f488f157918f484a9254fa7fa3348d4afbf/http_snapshot-0.1.9-py3-none-any.whl", hash = "sha256:62df93b7c429bd8d836e4dad48b36c473ca88d13652985267a01de9ec4c865c3", size = 12730, upload-time = "2026-02-24T21:11:27.059Z" }, ] [package.optional-dependencies] httpx = [ { name = "httpx" }, ] [[package]] name = "httpcore" version = "1.0.9" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "h11" }, ] sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } 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 = "httpx" version = "0.28.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "certifi" }, { name = "httpcore" }, { name = "idna" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, ] [[package]] name = "httpx-aiohttp" version = "0.1.12" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, { name = "httpx" }, ] sdist = { url = "https://files.pythonhosted.org/packages/63/2c/b894861cecf030fb45675ea24aa55b5722e97c602a163d872fca66c5a6d8/httpx_aiohttp-0.1.12.tar.gz", hash = "sha256:81feec51fd82c0ecfa0e9aaf1b1a6c2591260d5e2bcbeb7eb0277a78e610df2c", size = 275945, upload-time = "2025-12-12T10:12:15.283Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/16/8d/85c9701e9af72ca132a1783e2a54364a90c6da832304416a30fc11196ab2/httpx_aiohttp-0.1.12-py3-none-any.whl", hash = "sha256:5b0eac39a7f360fa7867a60bcb46bb1024eada9c01cbfecdb54dc1edb3fb7141", size = 6367, upload-time = "2025-12-12T10:12:14.018Z" }, ] [[package]] name = "httpx-sse" version = "0.4.3" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943, upload-time = "2025-10-10T21:48:22.271Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" }, ] [[package]] name = "idna" version = "3.11" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, ] [[package]] name = "importlib-metadata" version = "8.7.1" 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" } 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" }, ] [[package]] name = "iniconfig" version = "2.1.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.10'", ] sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, ] [[package]] name = "iniconfig" version = "2.3.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.14' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and python_full_version < '3.14' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and python_full_version < '3.14' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra != 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", ] sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, ] [[package]] name = "inline-snapshot" version = "0.32.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "asttokens" }, { name = "executing" }, { name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "pytest", version = "9.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "rich" }, { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ca/87/62b78b49042c533038ab1bf0931a7b70fdb78d07a11c9bf159be04027df8/inline_snapshot-0.32.5.tar.gz", hash = "sha256:5025074eab5c82a88504975e2655beeb5e96fd57ed2d9ebb38538473748f2065", size = 2626796, upload-time = "2026-03-13T18:35:54.891Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/09/d3/73426dd3da75095fd071ce5c1f8e520e879a582ca04df861575c6feb9166/inline_snapshot-0.32.5-py3-none-any.whl", hash = "sha256:ac617c273e811ed5ca15abd8f8dbd3fa268296bb0642ccb1403a5df61ce2e39e", size = 84993, upload-time = "2026-03-13T18:35:52.955Z" }, ] [[package]] name = "jiter" version = "0.13.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/0d/5e/4ec91646aee381d01cdb9974e30882c9cd3b8c5d1079d6b5ff4af522439a/jiter-0.13.0.tar.gz", hash = "sha256:f2839f9c2c7e2dffc1bc5929a510e14ce0a946be9365fd1219e7ef342dae14f4", size = 164847, upload-time = "2026-02-02T12:37:56.441Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/d0/5a/41da76c5ea07bec1b0472b6b2fdb1b651074d504b19374d7e130e0cdfb25/jiter-0.13.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2ffc63785fd6c7977defe49b9824ae6ce2b2e2b77ce539bdaf006c26da06342e", size = 311164, upload-time = "2026-02-02T12:35:17.688Z" }, { url = "https://files.pythonhosted.org/packages/40/cb/4a1bf994a3e869f0d39d10e11efb471b76d0ad70ecbfb591427a46c880c2/jiter-0.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4a638816427006c1e3f0013eb66d391d7a3acda99a7b0cf091eff4497ccea33a", size = 320296, upload-time = "2026-02-02T12:35:19.828Z" }, { url = "https://files.pythonhosted.org/packages/09/82/acd71ca9b50ecebadc3979c541cd717cce2fe2bc86236f4fa597565d8f1a/jiter-0.13.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19928b5d1ce0ff8c1ee1b9bdef3b5bfc19e8304f1b904e436caf30bc15dc6cf5", size = 352742, upload-time = "2026-02-02T12:35:21.258Z" }, { url = "https://files.pythonhosted.org/packages/71/03/d1fc996f3aecfd42eb70922edecfb6dd26421c874503e241153ad41df94f/jiter-0.13.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:309549b778b949d731a2f0e1594a3f805716be704a73bf3ad9a807eed5eb5721", size = 363145, upload-time = "2026-02-02T12:35:24.653Z" }, { url = "https://files.pythonhosted.org/packages/f1/61/a30492366378cc7a93088858f8991acd7d959759fe6138c12a4644e58e81/jiter-0.13.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bcdabaea26cb04e25df3103ce47f97466627999260290349a88c8136ecae0060", size = 487683, upload-time = "2026-02-02T12:35:26.162Z" }, { url = "https://files.pythonhosted.org/packages/20/4e/4223cffa9dbbbc96ed821c5aeb6bca510848c72c02086d1ed3f1da3d58a7/jiter-0.13.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a3a377af27b236abbf665a69b2bdd680e3b5a0bd2af825cd3b81245279a7606c", size = 373579, upload-time = "2026-02-02T12:35:27.582Z" }, { url = "https://files.pythonhosted.org/packages/fe/c9/b0489a01329ab07a83812d9ebcffe7820a38163c6d9e7da644f926ff877c/jiter-0.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe49d3ff6db74321f144dff9addd4a5874d3105ac5ba7c5b77fac099cfae31ae", size = 362904, upload-time = "2026-02-02T12:35:28.925Z" }, { url = "https://files.pythonhosted.org/packages/05/af/53e561352a44afcba9a9bc67ee1d320b05a370aed8df54eafe714c4e454d/jiter-0.13.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2113c17c9a67071b0f820733c0893ed1d467b5fcf4414068169e5c2cabddb1e2", size = 392380, upload-time = "2026-02-02T12:35:30.385Z" }, { url = "https://files.pythonhosted.org/packages/76/2a/dd805c3afb8ed5b326c5ae49e725d1b1255b9754b1b77dbecdc621b20773/jiter-0.13.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ab1185ca5c8b9491b55ebf6c1e8866b8f68258612899693e24a92c5fdb9455d5", size = 517939, upload-time = "2026-02-02T12:35:31.865Z" }, { url = "https://files.pythonhosted.org/packages/20/2a/7b67d76f55b8fe14c937e7640389612f05f9a4145fc28ae128aaa5e62257/jiter-0.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9621ca242547edc16400981ca3231e0c91c0c4c1ab8573a596cd9bb3575d5c2b", size = 551696, upload-time = "2026-02-02T12:35:33.306Z" }, { url = "https://files.pythonhosted.org/packages/85/9c/57cdd64dac8f4c6ab8f994fe0eb04dc9fd1db102856a4458fcf8a99dfa62/jiter-0.13.0-cp310-cp310-win32.whl", hash = "sha256:a7637d92b1c9d7a771e8c56f445c7f84396d48f2e756e5978840ecba2fac0894", size = 204592, upload-time = "2026-02-02T12:35:34.58Z" }, { url = "https://files.pythonhosted.org/packages/a7/38/f4f3ea5788b8a5bae7510a678cdc747eda0c45ffe534f9878ff37e7cf3b3/jiter-0.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c1b609e5cbd2f52bb74fb721515745b407df26d7b800458bd97cb3b972c29e7d", size = 206016, upload-time = "2026-02-02T12:35:36.435Z" }, { url = "https://files.pythonhosted.org/packages/71/29/499f8c9eaa8a16751b1c0e45e6f5f1761d180da873d417996cc7bddc8eef/jiter-0.13.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ea026e70a9a28ebbdddcbcf0f1323128a8db66898a06eaad3a4e62d2f554d096", size = 311157, upload-time = "2026-02-02T12:35:37.758Z" }, { url = "https://files.pythonhosted.org/packages/50/f6/566364c777d2ab450b92100bea11333c64c38d32caf8dc378b48e5b20c46/jiter-0.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:66aa3e663840152d18cc8ff1e4faad3dd181373491b9cfdc6004b92198d67911", size = 319729, upload-time = "2026-02-02T12:35:39.246Z" }, { url = "https://files.pythonhosted.org/packages/73/dd/560f13ec5e4f116d8ad2658781646cca91b617ae3b8758d4a5076b278f70/jiter-0.13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3524798e70655ff19aec58c7d05adb1f074fecff62da857ea9be2b908b6d701", size = 354766, upload-time = "2026-02-02T12:35:40.662Z" }, { url = "https://files.pythonhosted.org/packages/7c/0d/061faffcfe94608cbc28a0d42a77a74222bdf5055ccdbe5fd2292b94f510/jiter-0.13.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ec7e287d7fbd02cb6e22f9a00dd9c9cd504c40a61f2c61e7e1f9690a82726b4c", size = 362587, upload-time = "2026-02-02T12:35:42.025Z" }, { url = "https://files.pythonhosted.org/packages/92/c9/c66a7864982fd38a9773ec6e932e0398d1262677b8c60faecd02ffb67bf3/jiter-0.13.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:47455245307e4debf2ce6c6e65a717550a0244231240dcf3b8f7d64e4c2f22f4", size = 487537, upload-time = "2026-02-02T12:35:43.459Z" }, { url = "https://files.pythonhosted.org/packages/6c/86/84eb4352cd3668f16d1a88929b5888a3fe0418ea8c1dfc2ad4e7bf6e069a/jiter-0.13.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ee9da221dca6e0429c2704c1b3655fe7b025204a71d4d9b73390c759d776d165", size = 373717, upload-time = "2026-02-02T12:35:44.928Z" }, { url = "https://files.pythonhosted.org/packages/6e/09/9fe4c159358176f82d4390407a03f506a8659ed13ca3ac93a843402acecf/jiter-0.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24ab43126d5e05f3d53a36a8e11eb2f23304c6c1117844aaaf9a0aa5e40b5018", size = 362683, upload-time = "2026-02-02T12:35:46.636Z" }, { url = "https://files.pythonhosted.org/packages/c9/5e/85f3ab9caca0c1d0897937d378b4a515cae9e119730563572361ea0c48ae/jiter-0.13.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9da38b4fedde4fb528c740c2564628fbab737166a0e73d6d46cb4bb5463ff411", size = 392345, upload-time = "2026-02-02T12:35:48.088Z" }, { url = "https://files.pythonhosted.org/packages/12/4c/05b8629ad546191939e6f0c2f17e29f542a398f4a52fb987bc70b6d1eb8b/jiter-0.13.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0b34c519e17658ed88d5047999a93547f8889f3c1824120c26ad6be5f27b6cf5", size = 517775, upload-time = "2026-02-02T12:35:49.482Z" }, { url = "https://files.pythonhosted.org/packages/4d/88/367ea2eb6bc582c7052e4baf5ddf57ebe5ab924a88e0e09830dfb585c02d/jiter-0.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d2a6394e6af690d462310a86b53c47ad75ac8c21dc79f120714ea449979cb1d3", size = 551325, upload-time = "2026-02-02T12:35:51.104Z" }, { url = "https://files.pythonhosted.org/packages/f3/12/fa377ffb94a2f28c41afaed093e0d70cfe512035d5ecb0cad0ae4792d35e/jiter-0.13.0-cp311-cp311-win32.whl", hash = "sha256:0f0c065695f616a27c920a56ad0d4fc46415ef8b806bf8fc1cacf25002bd24e1", size = 204709, upload-time = "2026-02-02T12:35:52.467Z" }, { url = "https://files.pythonhosted.org/packages/cb/16/8e8203ce92f844dfcd3d9d6a5a7322c77077248dbb12da52d23193a839cd/jiter-0.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:0733312953b909688ae3c2d58d043aa040f9f1a6a75693defed7bc2cc4bf2654", size = 204560, upload-time = "2026-02-02T12:35:53.925Z" }, { url = "https://files.pythonhosted.org/packages/44/26/97cc40663deb17b9e13c3a5cf29251788c271b18ee4d262c8f94798b8336/jiter-0.13.0-cp311-cp311-win_arm64.whl", hash = "sha256:5d9b34ad56761b3bf0fbe8f7e55468704107608512350962d3317ffd7a4382d5", size = 189608, upload-time = "2026-02-02T12:35:55.304Z" }, { url = "https://files.pythonhosted.org/packages/2e/30/7687e4f87086829955013ca12a9233523349767f69653ebc27036313def9/jiter-0.13.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0a2bd69fc1d902e89925fc34d1da51b2128019423d7b339a45d9e99c894e0663", size = 307958, upload-time = "2026-02-02T12:35:57.165Z" }, { url = "https://files.pythonhosted.org/packages/c3/27/e57f9a783246ed95481e6749cc5002a8a767a73177a83c63ea71f0528b90/jiter-0.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f917a04240ef31898182f76a332f508f2cc4b57d2b4d7ad2dbfebbfe167eb505", size = 318597, upload-time = "2026-02-02T12:35:58.591Z" }, { url = "https://files.pythonhosted.org/packages/cf/52/e5719a60ac5d4d7c5995461a94ad5ef962a37c8bf5b088390e6fad59b2ff/jiter-0.13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1e2b199f446d3e82246b4fd9236d7cb502dc2222b18698ba0d986d2fecc6152", size = 348821, upload-time = "2026-02-02T12:36:00.093Z" }, { url = "https://files.pythonhosted.org/packages/61/db/c1efc32b8ba4c740ab3fc2d037d8753f67685f475e26b9d6536a4322bcdd/jiter-0.13.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04670992b576fa65bd056dbac0c39fe8bd67681c380cb2b48efa885711d9d726", size = 364163, upload-time = "2026-02-02T12:36:01.937Z" }, { url = "https://files.pythonhosted.org/packages/55/8a/fb75556236047c8806995671a18e4a0ad646ed255276f51a20f32dceaeec/jiter-0.13.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a1aff1fbdb803a376d4d22a8f63f8e7ccbce0b4890c26cc7af9e501ab339ef0", size = 483709, upload-time = "2026-02-02T12:36:03.41Z" }, { url = "https://files.pythonhosted.org/packages/7e/16/43512e6ee863875693a8e6f6d532e19d650779d6ba9a81593ae40a9088ff/jiter-0.13.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b3fb8c2053acaef8580809ac1d1f7481a0a0bdc012fd7f5d8b18fb696a5a089", size = 370480, upload-time = "2026-02-02T12:36:04.791Z" }, { url = "https://files.pythonhosted.org/packages/f8/4c/09b93e30e984a187bc8aaa3510e1ec8dcbdcd71ca05d2f56aac0492453aa/jiter-0.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdaba7d87e66f26a2c45d8cbadcbfc4bf7884182317907baf39cfe9775bb4d93", size = 360735, upload-time = "2026-02-02T12:36:06.994Z" }, { url = "https://files.pythonhosted.org/packages/1a/1b/46c5e349019874ec5dfa508c14c37e29864ea108d376ae26d90bee238cd7/jiter-0.13.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7b88d649135aca526da172e48083da915ec086b54e8e73a425ba50999468cc08", size = 391814, upload-time = "2026-02-02T12:36:08.368Z" }, { url = "https://files.pythonhosted.org/packages/15/9e/26184760e85baee7162ad37b7912797d2077718476bf91517641c92b3639/jiter-0.13.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e404ea551d35438013c64b4f357b0474c7abf9f781c06d44fcaf7a14c69ff9e2", size = 513990, upload-time = "2026-02-02T12:36:09.993Z" }, { url = "https://files.pythonhosted.org/packages/e9/34/2c9355247d6debad57a0a15e76ab1566ab799388042743656e566b3b7de1/jiter-0.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1f4748aad1b4a93c8bdd70f604d0f748cdc0e8744c5547798acfa52f10e79228", size = 548021, upload-time = "2026-02-02T12:36:11.376Z" }, { url = "https://files.pythonhosted.org/packages/ac/4a/9f2c23255d04a834398b9c2e0e665382116911dc4d06b795710503cdad25/jiter-0.13.0-cp312-cp312-win32.whl", hash = "sha256:0bf670e3b1445fc4d31612199f1744f67f889ee1bbae703c4b54dc097e5dd394", size = 203024, upload-time = "2026-02-02T12:36:12.682Z" }, { url = "https://files.pythonhosted.org/packages/09/ee/f0ae675a957ae5a8f160be3e87acea6b11dc7b89f6b7ab057e77b2d2b13a/jiter-0.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:15db60e121e11fe186c0b15236bd5d18381b9ddacdcf4e659feb96fc6c969c92", size = 205424, upload-time = "2026-02-02T12:36:13.93Z" }, { url = "https://files.pythonhosted.org/packages/1b/02/ae611edf913d3cbf02c97cdb90374af2082c48d7190d74c1111dde08bcdd/jiter-0.13.0-cp312-cp312-win_arm64.whl", hash = "sha256:41f92313d17989102f3cb5dd533a02787cdb99454d494344b0361355da52fcb9", size = 186818, upload-time = "2026-02-02T12:36:15.308Z" }, { url = "https://files.pythonhosted.org/packages/91/9c/7ee5a6ff4b9991e1a45263bfc46731634c4a2bde27dfda6c8251df2d958c/jiter-0.13.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1f8a55b848cbabf97d861495cd65f1e5c590246fabca8b48e1747c4dfc8f85bf", size = 306897, upload-time = "2026-02-02T12:36:16.748Z" }, { url = "https://files.pythonhosted.org/packages/7c/02/be5b870d1d2be5dd6a91bdfb90f248fbb7dcbd21338f092c6b89817c3dbf/jiter-0.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f556aa591c00f2c45eb1b89f68f52441a016034d18b65da60e2d2875bbbf344a", size = 317507, upload-time = "2026-02-02T12:36:18.351Z" }, { url = "https://files.pythonhosted.org/packages/da/92/b25d2ec333615f5f284f3a4024f7ce68cfa0604c322c6808b2344c7f5d2b/jiter-0.13.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7e1d61da332ec412350463891923f960c3073cf1aae93b538f0bb4c8cd46efb", size = 350560, upload-time = "2026-02-02T12:36:19.746Z" }, { url = "https://files.pythonhosted.org/packages/be/ec/74dcb99fef0aca9fbe56b303bf79f6bd839010cb18ad41000bf6cc71eec0/jiter-0.13.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3097d665a27bc96fd9bbf7f86178037db139f319f785e4757ce7ccbf390db6c2", size = 363232, upload-time = "2026-02-02T12:36:21.243Z" }, { url = "https://files.pythonhosted.org/packages/1b/37/f17375e0bb2f6a812d4dd92d7616e41917f740f3e71343627da9db2824ce/jiter-0.13.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d01ecc3a8cbdb6f25a37bd500510550b64ddf9f7d64a107d92f3ccb25035d0f", size = 483727, upload-time = "2026-02-02T12:36:22.688Z" }, { url = "https://files.pythonhosted.org/packages/77/d2/a71160a5ae1a1e66c1395b37ef77da67513b0adba73b993a27fbe47eb048/jiter-0.13.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed9bbc30f5d60a3bdf63ae76beb3f9db280d7f195dfcfa61af792d6ce912d159", size = 370799, upload-time = "2026-02-02T12:36:24.106Z" }, { url = "https://files.pythonhosted.org/packages/01/99/ed5e478ff0eb4e8aa5fd998f9d69603c9fd3f32de3bd16c2b1194f68361c/jiter-0.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98fbafb6e88256f4454de33c1f40203d09fc33ed19162a68b3b257b29ca7f663", size = 359120, upload-time = "2026-02-02T12:36:25.519Z" }, { url = "https://files.pythonhosted.org/packages/16/be/7ffd08203277a813f732ba897352797fa9493faf8dc7995b31f3d9cb9488/jiter-0.13.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5467696f6b827f1116556cb0db620440380434591e93ecee7fd14d1a491b6daa", size = 390664, upload-time = "2026-02-02T12:36:26.866Z" }, { url = "https://files.pythonhosted.org/packages/d1/84/e0787856196d6d346264d6dcccb01f741e5f0bd014c1d9a2ebe149caf4f3/jiter-0.13.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:2d08c9475d48b92892583df9da592a0e2ac49bcd41fae1fec4f39ba6cf107820", size = 513543, upload-time = "2026-02-02T12:36:28.217Z" }, { url = "https://files.pythonhosted.org/packages/65/50/ecbd258181c4313cf79bca6c88fb63207d04d5bf5e4f65174114d072aa55/jiter-0.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:aed40e099404721d7fcaf5b89bd3b4568a4666358bcac7b6b15c09fb6252ab68", size = 547262, upload-time = "2026-02-02T12:36:29.678Z" }, { url = "https://files.pythonhosted.org/packages/27/da/68f38d12e7111d2016cd198161b36e1f042bd115c169255bcb7ec823a3bf/jiter-0.13.0-cp313-cp313-win32.whl", hash = "sha256:36ebfbcffafb146d0e6ffb3e74d51e03d9c35ce7c625c8066cdbfc7b953bdc72", size = 200630, upload-time = "2026-02-02T12:36:31.808Z" }, { url = "https://files.pythonhosted.org/packages/25/65/3bd1a972c9a08ecd22eb3b08a95d1941ebe6938aea620c246cf426ae09c2/jiter-0.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:8d76029f077379374cf0dbc78dbe45b38dec4a2eb78b08b5194ce836b2517afc", size = 202602, upload-time = "2026-02-02T12:36:33.679Z" }, { url = "https://files.pythonhosted.org/packages/15/fe/13bd3678a311aa67686bb303654792c48206a112068f8b0b21426eb6851e/jiter-0.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:bb7613e1a427cfcb6ea4544f9ac566b93d5bf67e0d48c787eca673ff9c9dff2b", size = 185939, upload-time = "2026-02-02T12:36:35.065Z" }, { url = "https://files.pythonhosted.org/packages/49/19/a929ec002ad3228bc97ca01dbb14f7632fffdc84a95ec92ceaf4145688ae/jiter-0.13.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fa476ab5dd49f3bf3a168e05f89358c75a17608dbabb080ef65f96b27c19ab10", size = 316616, upload-time = "2026-02-02T12:36:36.579Z" }, { url = "https://files.pythonhosted.org/packages/52/56/d19a9a194afa37c1728831e5fb81b7722c3de18a3109e8f282bfc23e587a/jiter-0.13.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ade8cb6ff5632a62b7dbd4757d8c5573f7a2e9ae285d6b5b841707d8363205ef", size = 346850, upload-time = "2026-02-02T12:36:38.058Z" }, { url = "https://files.pythonhosted.org/packages/36/4a/94e831c6bf287754a8a019cb966ed39ff8be6ab78cadecf08df3bb02d505/jiter-0.13.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9950290340acc1adaded363edd94baebcee7dabdfa8bee4790794cd5cfad2af6", size = 358551, upload-time = "2026-02-02T12:36:39.417Z" }, { url = "https://files.pythonhosted.org/packages/a2/ec/a4c72c822695fa80e55d2b4142b73f0012035d9fcf90eccc56bc060db37c/jiter-0.13.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2b4972c6df33731aac0742b64fd0d18e0a69bc7d6e03108ce7d40c85fd9e3e6d", size = 201950, upload-time = "2026-02-02T12:36:40.791Z" }, { url = "https://files.pythonhosted.org/packages/b6/00/393553ec27b824fbc29047e9c7cd4a3951d7fbe4a76743f17e44034fa4e4/jiter-0.13.0-cp313-cp313t-win_arm64.whl", hash = "sha256:701a1e77d1e593c1b435315ff625fd071f0998c5f02792038a5ca98899261b7d", size = 185852, upload-time = "2026-02-02T12:36:42.077Z" }, { url = "https://files.pythonhosted.org/packages/6e/f5/f1997e987211f6f9bd71b8083047b316208b4aca0b529bb5f8c96c89ef3e/jiter-0.13.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:cc5223ab19fe25e2f0bf2643204ad7318896fe3729bf12fde41b77bfc4fafff0", size = 308804, upload-time = "2026-02-02T12:36:43.496Z" }, { url = "https://files.pythonhosted.org/packages/cd/8f/5482a7677731fd44881f0204981ce2d7175db271f82cba2085dd2212e095/jiter-0.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9776ebe51713acf438fd9b4405fcd86893ae5d03487546dae7f34993217f8a91", size = 318787, upload-time = "2026-02-02T12:36:45.071Z" }, { url = "https://files.pythonhosted.org/packages/f3/b9/7257ac59778f1cd025b26a23c5520a36a424f7f1b068f2442a5b499b7464/jiter-0.13.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:879e768938e7b49b5e90b7e3fecc0dbec01b8cb89595861fb39a8967c5220d09", size = 353880, upload-time = "2026-02-02T12:36:47.365Z" }, { url = "https://files.pythonhosted.org/packages/c3/87/719eec4a3f0841dad99e3d3604ee4cba36af4419a76f3cb0b8e2e691ad67/jiter-0.13.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:682161a67adea11e3aae9038c06c8b4a9a71023228767477d683f69903ebc607", size = 366702, upload-time = "2026-02-02T12:36:48.871Z" }, { url = "https://files.pythonhosted.org/packages/d2/65/415f0a75cf6921e43365a1bc227c565cb949caca8b7532776e430cbaa530/jiter-0.13.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a13b68cd1cd8cc9de8f244ebae18ccb3e4067ad205220ef324c39181e23bbf66", size = 486319, upload-time = "2026-02-02T12:36:53.006Z" }, { url = "https://files.pythonhosted.org/packages/54/a2/9e12b48e82c6bbc6081fd81abf915e1443add1b13d8fc586e1d90bb02bb8/jiter-0.13.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87ce0f14c6c08892b610686ae8be350bf368467b6acd5085a5b65441e2bf36d2", size = 372289, upload-time = "2026-02-02T12:36:54.593Z" }, { url = "https://files.pythonhosted.org/packages/4e/c1/e4693f107a1789a239c759a432e9afc592366f04e901470c2af89cfd28e1/jiter-0.13.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c365005b05505a90d1c47856420980d0237adf82f70c4aff7aebd3c1cc143ad", size = 360165, upload-time = "2026-02-02T12:36:56.112Z" }, { url = "https://files.pythonhosted.org/packages/17/08/91b9ea976c1c758240614bd88442681a87672eebc3d9a6dde476874e706b/jiter-0.13.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1317fdffd16f5873e46ce27d0e0f7f4f90f0cdf1d86bf6abeaea9f63ca2c401d", size = 389634, upload-time = "2026-02-02T12:36:57.495Z" }, { url = "https://files.pythonhosted.org/packages/18/23/58325ef99390d6d40427ed6005bf1ad54f2577866594bcf13ce55675f87d/jiter-0.13.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:c05b450d37ba0c9e21c77fef1f205f56bcee2330bddca68d344baebfc55ae0df", size = 514933, upload-time = "2026-02-02T12:36:58.909Z" }, { url = "https://files.pythonhosted.org/packages/5b/25/69f1120c7c395fd276c3996bb8adefa9c6b84c12bb7111e5c6ccdcd8526d/jiter-0.13.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:775e10de3849d0631a97c603f996f518159272db00fdda0a780f81752255ee9d", size = 548842, upload-time = "2026-02-02T12:37:00.433Z" }, { url = "https://files.pythonhosted.org/packages/18/05/981c9669d86850c5fbb0d9e62bba144787f9fba84546ba43d624ee27ef29/jiter-0.13.0-cp314-cp314-win32.whl", hash = "sha256:632bf7c1d28421c00dd8bbb8a3bac5663e1f57d5cd5ed962bce3c73bf62608e6", size = 202108, upload-time = "2026-02-02T12:37:01.718Z" }, { url = "https://files.pythonhosted.org/packages/8d/96/cdcf54dd0b0341db7d25413229888a346c7130bd20820530905fdb65727b/jiter-0.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:f22ef501c3f87ede88f23f9b11e608581c14f04db59b6a801f354397ae13739f", size = 204027, upload-time = "2026-02-02T12:37:03.075Z" }, { url = "https://files.pythonhosted.org/packages/fb/f9/724bcaaab7a3cd727031fe4f6995cb86c4bd344909177c186699c8dec51a/jiter-0.13.0-cp314-cp314-win_arm64.whl", hash = "sha256:07b75fe09a4ee8e0c606200622e571e44943f47254f95e2436c8bdcaceb36d7d", size = 187199, upload-time = "2026-02-02T12:37:04.414Z" }, { url = "https://files.pythonhosted.org/packages/62/92/1661d8b9fd6a3d7a2d89831db26fe3c1509a287d83ad7838831c7b7a5c7e/jiter-0.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:964538479359059a35fb400e769295d4b315ae61e4105396d355a12f7fef09f0", size = 318423, upload-time = "2026-02-02T12:37:05.806Z" }, { url = "https://files.pythonhosted.org/packages/4f/3b/f77d342a54d4ebcd128e520fc58ec2f5b30a423b0fd26acdfc0c6fef8e26/jiter-0.13.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e104da1db1c0991b3eaed391ccd650ae8d947eab1480c733e5a3fb28d4313e40", size = 351438, upload-time = "2026-02-02T12:37:07.189Z" }, { url = "https://files.pythonhosted.org/packages/76/b3/ba9a69f0e4209bd3331470c723c2f5509e6f0482e416b612431a5061ed71/jiter-0.13.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e3a5f0cde8ff433b8e88e41aa40131455420fb3649a3c7abdda6145f8cb7202", size = 364774, upload-time = "2026-02-02T12:37:08.579Z" }, { url = "https://files.pythonhosted.org/packages/b3/16/6cdb31fa342932602458dbb631bfbd47f601e03d2e4950740e0b2100b570/jiter-0.13.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:57aab48f40be1db920a582b30b116fe2435d184f77f0e4226f546794cedd9cf0", size = 487238, upload-time = "2026-02-02T12:37:10.066Z" }, { url = "https://files.pythonhosted.org/packages/ed/b1/956cc7abaca8d95c13aa8d6c9b3f3797241c246cd6e792934cc4c8b250d2/jiter-0.13.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7772115877c53f62beeb8fd853cab692dbc04374ef623b30f997959a4c0e7e95", size = 372892, upload-time = "2026-02-02T12:37:11.656Z" }, { url = "https://files.pythonhosted.org/packages/26/c4/97ecde8b1e74f67b8598c57c6fccf6df86ea7861ed29da84629cdbba76c4/jiter-0.13.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1211427574b17b633cfceba5040de8081e5abf114f7a7602f73d2e16f9fdaa59", size = 360309, upload-time = "2026-02-02T12:37:13.244Z" }, { url = "https://files.pythonhosted.org/packages/4b/d7/eabe3cf46715854ccc80be2cd78dd4c36aedeb30751dbf85a1d08c14373c/jiter-0.13.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7beae3a3d3b5212d3a55d2961db3c292e02e302feb43fce6a3f7a31b90ea6dfe", size = 389607, upload-time = "2026-02-02T12:37:14.881Z" }, { url = "https://files.pythonhosted.org/packages/df/2d/03963fc0804e6109b82decfb9974eb92df3797fe7222428cae12f8ccaa0c/jiter-0.13.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:e5562a0f0e90a6223b704163ea28e831bd3a9faa3512a711f031611e6b06c939", size = 514986, upload-time = "2026-02-02T12:37:16.326Z" }, { url = "https://files.pythonhosted.org/packages/f6/6c/8c83b45eb3eb1c1e18d841fe30b4b5bc5619d781267ca9bc03e005d8fd0a/jiter-0.13.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:6c26a424569a59140fb51160a56df13f438a2b0967365e987889186d5fc2f6f9", size = 548756, upload-time = "2026-02-02T12:37:17.736Z" }, { url = "https://files.pythonhosted.org/packages/47/66/eea81dfff765ed66c68fd2ed8c96245109e13c896c2a5015c7839c92367e/jiter-0.13.0-cp314-cp314t-win32.whl", hash = "sha256:24dc96eca9f84da4131cdf87a95e6ce36765c3b156fc9ae33280873b1c32d5f6", size = 201196, upload-time = "2026-02-02T12:37:19.101Z" }, { url = "https://files.pythonhosted.org/packages/ff/32/4ac9c7a76402f8f00d00842a7f6b83b284d0cf7c1e9d4227bc95aa6d17fa/jiter-0.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0a8d76c7524087272c8ae913f5d9d608bd839154b62c4322ef65723d2e5bb0b8", size = 204215, upload-time = "2026-02-02T12:37:20.495Z" }, { url = "https://files.pythonhosted.org/packages/f9/8e/7def204fea9f9be8b3c21a6f2dd6c020cf56c7d5ff753e0e23ed7f9ea57e/jiter-0.13.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2c26cf47e2cad140fa23b6d58d435a7c0161f5c514284802f25e87fddfe11024", size = 187152, upload-time = "2026-02-02T12:37:22.124Z" }, { url = "https://files.pythonhosted.org/packages/41/95/8e6611379c9ce8534ff94dd800c50d6d0061b2c9ae6386fbcd86c7386f0a/jiter-0.13.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:4397ee562b9f69d283e5674445551b47a5e8076fdde75e71bfac5891113dc543", size = 313635, upload-time = "2026-02-02T12:37:23.545Z" }, { url = "https://files.pythonhosted.org/packages/70/ea/17db64dcaf84bbb187874232222030ea4d689e6008f93bda6e7c691bc4c7/jiter-0.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f90023f8f672e13ea1819507d2d21b9d2d1c18920a3b3a5f1541955a85b5504", size = 309761, upload-time = "2026-02-02T12:37:25.075Z" }, { url = "https://files.pythonhosted.org/packages/a3/36/b2e2a7b12b94ecc7248acf2a8fe6288be893d1ebb9728655ceada22f00ad/jiter-0.13.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed0240dd1536a98c3ab55e929c60dfff7c899fecafcb7d01161b21a99fc8c363", size = 355245, upload-time = "2026-02-02T12:37:26.646Z" }, { url = "https://files.pythonhosted.org/packages/77/3f/5b159663c5be622daec20074c997bb66bc1fac63c167c02aef3df476fb32/jiter-0.13.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6207fc61c395b26fffdcf637a0b06b4326f35bfa93c6e92fe1a166a21aeb6731", size = 365842, upload-time = "2026-02-02T12:37:28.207Z" }, { url = "https://files.pythonhosted.org/packages/98/30/76a68fa2c9c815c6b7802a92fc354080d66ffba9acc4690fd85622f77ad4/jiter-0.13.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00203f47c214156df427b5989de74cb340c65c8180d09be1bf9de81d0abad599", size = 489223, upload-time = "2026-02-02T12:37:29.571Z" }, { url = "https://files.pythonhosted.org/packages/a3/39/7c5cb85ccd71241513c878054c26a55828ccded6567d931a23ea4be73787/jiter-0.13.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c26ad6967c9dcedf10c995a21539c3aa57d4abad7001b7a84f621a263a6b605", size = 375762, upload-time = "2026-02-02T12:37:31.186Z" }, { url = "https://files.pythonhosted.org/packages/a8/6a/381cd18d050b0102e60324e8d3f51f37ef02c56e9f4e5f0b7d26ba18958d/jiter-0.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a576f5dce9ac7de5d350b8e2f552cf364f32975ed84717c35379a51c7cb198bd", size = 364996, upload-time = "2026-02-02T12:37:32.931Z" }, { url = "https://files.pythonhosted.org/packages/37/1e/d66310f1f7085c13ea6f1119c9566ec5d2cfd1dc90df963118a6869247bb/jiter-0.13.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b22945be8425d161f2e536cdae66da300b6b000f1c0ba3ddf237d1bfd45d21b8", size = 395463, upload-time = "2026-02-02T12:37:34.446Z" }, { url = "https://files.pythonhosted.org/packages/c0/ab/06ae77cb293f860b152c356c635c15aaa800ce48772865a41704d9fac80d/jiter-0.13.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6eeb7db8bc77dc20476bc2f7407a23dbe3d46d9cc664b166e3d474e1c1de4baa", size = 520944, upload-time = "2026-02-02T12:37:35.987Z" }, { url = "https://files.pythonhosted.org/packages/f1/8e/57b49b20361c42a80d455a6d83cb38626204508cab4298d6a22880205319/jiter-0.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:19cd6f85e1dc090277c3ce90a5b7d96f32127681d825e71c9dce28788e39fc0c", size = 554955, upload-time = "2026-02-02T12:37:37.656Z" }, { url = "https://files.pythonhosted.org/packages/79/dd/113489973c3b4256e383321aea11bd57389e401912fa48eb145a99b38767/jiter-0.13.0-cp39-cp39-win32.whl", hash = "sha256:dc3ce84cfd4fa9628fe62c4f85d0d597a4627d4242cfafac32a12cc1455d00f7", size = 206876, upload-time = "2026-02-02T12:37:39.225Z" }, { url = "https://files.pythonhosted.org/packages/6e/73/2bdfc7133c5ee0c8f18cfe4a7582f3cfbbf3ff672cec1b5f4ca67ff9d041/jiter-0.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:9ffda299e417dc83362963966c50cb76d42da673ee140de8a8ac762d4bb2378b", size = 206404, upload-time = "2026-02-02T12:37:40.632Z" }, { url = "https://files.pythonhosted.org/packages/79/b3/3c29819a27178d0e461a8571fb63c6ae38be6dc36b78b3ec2876bbd6a910/jiter-0.13.0-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b1cbfa133241d0e6bdab48dcdc2604e8ba81512f6bbd68ec3e8e1357dd3c316c", size = 307016, upload-time = "2026-02-02T12:37:42.755Z" }, { url = "https://files.pythonhosted.org/packages/eb/ae/60993e4b07b1ac5ebe46da7aa99fdbb802eb986c38d26e3883ac0125c4e0/jiter-0.13.0-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:db367d8be9fad6e8ebbac4a7578b7af562e506211036cba2c06c3b998603c3d2", size = 305024, upload-time = "2026-02-02T12:37:44.774Z" }, { url = "https://files.pythonhosted.org/packages/77/fa/2227e590e9cf98803db2811f172b2d6460a21539ab73006f251c66f44b14/jiter-0.13.0-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45f6f8efb2f3b0603092401dc2df79fa89ccbc027aaba4174d2d4133ed661434", size = 339337, upload-time = "2026-02-02T12:37:46.668Z" }, { url = "https://files.pythonhosted.org/packages/2d/92/015173281f7eb96c0ef580c997da8ef50870d4f7f4c9e03c845a1d62ae04/jiter-0.13.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:597245258e6ad085d064780abfb23a284d418d3e61c57362d9449c6c7317ee2d", size = 346395, upload-time = "2026-02-02T12:37:48.09Z" }, { url = "https://files.pythonhosted.org/packages/80/60/e50fa45dd7e2eae049f0ce964663849e897300433921198aef94b6ffa23a/jiter-0.13.0-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:3d744a6061afba08dd7ae375dcde870cffb14429b7477e10f67e9e6d68772a0a", size = 305169, upload-time = "2026-02-02T12:37:50.376Z" }, { url = "https://files.pythonhosted.org/packages/d2/73/a009f41c5eed71c49bec53036c4b33555afcdee70682a18c6f66e396c039/jiter-0.13.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:ff732bd0a0e778f43d5009840f20b935e79087b4dc65bd36f1cd0f9b04b8ff7f", size = 303808, upload-time = "2026-02-02T12:37:52.092Z" }, { url = "https://files.pythonhosted.org/packages/c4/10/528b439290763bff3d939268085d03382471b442f212dca4ff5f12802d43/jiter-0.13.0-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab44b178f7981fcaea7e0a5df20e773c663d06ffda0198f1a524e91b2fde7e59", size = 337384, upload-time = "2026-02-02T12:37:53.582Z" }, { url = "https://files.pythonhosted.org/packages/67/8a/a342b2f0251f3dac4ca17618265d93bf244a2a4d089126e81e4c1056ac50/jiter-0.13.0-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bb00b6d26db67a05fe3e12c76edc75f32077fb51deed13822dc648fa373bc19", size = 343768, upload-time = "2026-02-02T12:37:55.055Z" }, ] [[package]] name = "jmespath" version = "1.1.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/d3/59/322338183ecda247fb5d1763a6cbe46eff7222eaeebafd9fa65d4bf5cb11/jmespath-1.1.0.tar.gz", hash = "sha256:472c87d80f36026ae83c6ddd0f1d05d4e510134ed462851fd5f754c8c3cbb88d", size = 27377, upload-time = "2026-01-22T16:35:26.279Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl", hash = "sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64", size = 20419, upload-time = "2026-01-22T16:35:24.919Z" }, ] [[package]] name = "jsonschema" version = "4.26.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs", marker = "python_full_version >= '3.10'" }, { name = "jsonschema-specifications", marker = "python_full_version >= '3.10'" }, { name = "referencing", marker = "python_full_version >= '3.10'" }, { name = "rpds-py", marker = "python_full_version >= '3.10'" }, ] 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 = [ { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, ] [[package]] name = "jsonschema-specifications" version = "2025.9.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "referencing", marker = "python_full_version >= '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, ] [[package]] name = "markdown-it-py" version = "3.0.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ { name = "mdurl", marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" }, ] [[package]] name = "markdown-it-py" version = "4.0.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.14' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and python_full_version < '3.14' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and python_full_version < '3.14' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra != 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", ] dependencies = [ { name = "mdurl", marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, ] [[package]] name = "mcp" version = "1.26.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio", marker = "python_full_version >= '3.10'" }, { name = "httpx", marker = "python_full_version >= '3.10'" }, { name = "httpx-sse", marker = "python_full_version >= '3.10'" }, { name = "jsonschema", marker = "python_full_version >= '3.10'" }, { name = "pydantic", version = "2.12.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "pydantic-settings", marker = "python_full_version >= '3.10'" }, { name = "pyjwt", extra = ["crypto"], marker = "(python_full_version >= '3.10' and extra == 'extra-9-anthropic-mcp') or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "python-multipart", marker = "python_full_version >= '3.10'" }, { name = "pywin32", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, { name = "sse-starlette", marker = "python_full_version >= '3.10'" }, { name = "starlette", marker = "python_full_version >= '3.10'" }, { name = "typing-extensions", marker = "python_full_version >= '3.10'" }, { name = "typing-inspection", marker = "python_full_version >= '3.10'" }, { name = "uvicorn", marker = "python_full_version >= '3.10' and sys_platform != 'emscripten'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/fc/6d/62e76bbb8144d6ed86e202b5edd8a4cb631e7c8130f3f4893c3f90262b10/mcp-1.26.0.tar.gz", hash = "sha256:db6e2ef491eecc1a0d93711a76f28dec2e05999f93afd48795da1c1137142c66", size = 608005, upload-time = "2026-01-24T19:40:32.468Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/fd/d9/eaa1f80170d2b7c5ba23f3b59f766f3a0bb41155fbc32a69adfa1adaaef9/mcp-1.26.0-py3-none-any.whl", hash = "sha256:904a21c33c25aa98ddbeb47273033c435e595bbacfdb177f4bd87f6dceebe1ca", size = 233615, upload-time = "2026-01-24T19:40:30.652Z" }, ] [[package]] name = "mdurl" version = "0.1.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } 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 = "multidict" version = "6.7.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions", marker = "python_full_version < '3.11' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/84/0b/19348d4c98980c4851d2f943f8ebafdece2ae7ef737adcfa5994ce8e5f10/multidict-6.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c93c3db7ea657dd4637d57e74ab73de31bccefe144d3d4ce370052035bc85fb5", size = 77176, upload-time = "2026-01-26T02:42:59.784Z" }, { url = "https://files.pythonhosted.org/packages/ef/04/9de3f8077852e3d438215c81e9b691244532d2e05b4270e89ce67b7d103c/multidict-6.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:974e72a2474600827abaeda71af0c53d9ebbc3c2eb7da37b37d7829ae31232d8", size = 44996, upload-time = "2026-01-26T02:43:01.674Z" }, { url = "https://files.pythonhosted.org/packages/31/5c/08c7f7fe311f32e83f7621cd3f99d805f45519cd06fafb247628b861da7d/multidict-6.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdea2e7b2456cfb6694fb113066fd0ec7ea4d67e3a35e1f4cbeea0b448bf5872", size = 44631, upload-time = "2026-01-26T02:43:03.169Z" }, { url = "https://files.pythonhosted.org/packages/b7/7f/0e3b1390ae772f27501199996b94b52ceeb64fe6f9120a32c6c3f6b781be/multidict-6.7.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17207077e29342fdc2c9a82e4b306f1127bf1ea91f8b71e02d4798a70bb99991", size = 242561, upload-time = "2026-01-26T02:43:04.733Z" }, { url = "https://files.pythonhosted.org/packages/dd/f4/8719f4f167586af317b69dd3e90f913416c91ca610cac79a45c53f590312/multidict-6.7.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4f49cb5661344764e4c7c7973e92a47a59b8fc19b6523649ec9dc4960e58a03", size = 242223, upload-time = "2026-01-26T02:43:06.695Z" }, { url = "https://files.pythonhosted.org/packages/47/ab/7c36164cce64a6ad19c6d9a85377b7178ecf3b89f8fd589c73381a5eedfd/multidict-6.7.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a9fc4caa29e2e6ae408d1c450ac8bf19892c5fca83ee634ecd88a53332c59981", size = 222322, upload-time = "2026-01-26T02:43:08.472Z" }, { url = "https://files.pythonhosted.org/packages/f5/79/a25add6fb38035b5337bc5734f296d9afc99163403bbcf56d4170f97eb62/multidict-6.7.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c5f0c21549ab432b57dcc82130f388d84ad8179824cc3f223d5e7cfbfd4143f6", size = 254005, upload-time = "2026-01-26T02:43:10.127Z" }, { url = "https://files.pythonhosted.org/packages/4a/7b/64a87cf98e12f756fc8bd444b001232ffff2be37288f018ad0d3f0aae931/multidict-6.7.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7dfb78d966b2c906ae1d28ccf6e6712a3cd04407ee5088cd276fe8cb42186190", size = 251173, upload-time = "2026-01-26T02:43:11.731Z" }, { url = "https://files.pythonhosted.org/packages/4b/ac/b605473de2bb404e742f2cc3583d12aedb2352a70e49ae8fce455b50c5aa/multidict-6.7.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b0d9b91d1aa44db9c1f1ecd0d9d2ae610b2f4f856448664e01a3b35899f3f92", size = 243273, upload-time = "2026-01-26T02:43:13.063Z" }, { url = "https://files.pythonhosted.org/packages/03/65/11492d6a0e259783720f3bc1d9ea55579a76f1407e31ed44045c99542004/multidict-6.7.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dd96c01a9dcd4889dcfcf9eb5544ca0c77603f239e3ffab0524ec17aea9a93ee", size = 238956, upload-time = "2026-01-26T02:43:14.843Z" }, { url = "https://files.pythonhosted.org/packages/5f/a7/7ee591302af64e7c196fb63fe856c788993c1372df765102bd0448e7e165/multidict-6.7.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:067343c68cd6612d375710f895337b3a98a033c94f14b9a99eff902f205424e2", size = 233477, upload-time = "2026-01-26T02:43:16.025Z" }, { url = "https://files.pythonhosted.org/packages/9c/99/c109962d58756c35fd9992fed7f2355303846ea2ff054bb5f5e9d6b888de/multidict-6.7.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5884a04f4ff56c6120f6ccf703bdeb8b5079d808ba604d4d53aec0d55dc33568", size = 243615, upload-time = "2026-01-26T02:43:17.84Z" }, { url = "https://files.pythonhosted.org/packages/d5/5f/1973e7c771c86e93dcfe1c9cc55a5481b610f6614acfc28c0d326fe6bfad/multidict-6.7.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8affcf1c98b82bc901702eb73b6947a1bfa170823c153fe8a47b5f5f02e48e40", size = 249930, upload-time = "2026-01-26T02:43:19.06Z" }, { url = "https://files.pythonhosted.org/packages/5d/a5/f170fc2268c3243853580203378cd522446b2df632061e0a5409817854c7/multidict-6.7.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0d17522c37d03e85c8098ec8431636309b2682cf12e58f4dbc76121fb50e4962", size = 243807, upload-time = "2026-01-26T02:43:20.286Z" }, { url = "https://files.pythonhosted.org/packages/de/01/73856fab6d125e5bc652c3986b90e8699a95e84b48d72f39ade6c0e74a8c/multidict-6.7.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:24c0cf81544ca5e17cfcb6e482e7a82cd475925242b308b890c9452a074d4505", size = 239103, upload-time = "2026-01-26T02:43:21.508Z" }, { url = "https://files.pythonhosted.org/packages/e7/46/f1220bd9944d8aa40d8ccff100eeeee19b505b857b6f603d6078cb5315b0/multidict-6.7.1-cp310-cp310-win32.whl", hash = "sha256:d82dd730a95e6643802f4454b8fdecdf08667881a9c5670db85bc5a56693f122", size = 41416, upload-time = "2026-01-26T02:43:22.703Z" }, { url = "https://files.pythonhosted.org/packages/68/00/9b38e272a770303692fc406c36e1a4c740f401522d5787691eb38a8925a8/multidict-6.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:cf37cbe5ced48d417ba045aca1b21bafca67489452debcde94778a576666a1df", size = 46022, upload-time = "2026-01-26T02:43:23.77Z" }, { url = "https://files.pythonhosted.org/packages/64/65/d8d42490c02ee07b6bbe00f7190d70bb4738b3cce7629aaf9f213ef730dd/multidict-6.7.1-cp310-cp310-win_arm64.whl", hash = "sha256:59bc83d3f66b41dac1e7460aac1d196edc70c9ba3094965c467715a70ecb46db", size = 43238, upload-time = "2026-01-26T02:43:24.882Z" }, { url = "https://files.pythonhosted.org/packages/ce/f1/a90635c4f88fb913fbf4ce660b83b7445b7a02615bda034b2f8eb38fd597/multidict-6.7.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ff981b266af91d7b4b3793ca3382e53229088d193a85dfad6f5f4c27fc73e5d", size = 76626, upload-time = "2026-01-26T02:43:26.485Z" }, { url = "https://files.pythonhosted.org/packages/a6/9b/267e64eaf6fc637a15b35f5de31a566634a2740f97d8d094a69d34f524a4/multidict-6.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:844c5bca0b5444adb44a623fb0a1310c2f4cd41f402126bb269cd44c9b3f3e1e", size = 44706, upload-time = "2026-01-26T02:43:27.607Z" }, { url = "https://files.pythonhosted.org/packages/dd/a4/d45caf2b97b035c57267791ecfaafbd59c68212004b3842830954bb4b02e/multidict-6.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f2a0a924d4c2e9afcd7ec64f9de35fcd96915149b2216e1cb2c10a56df483855", size = 44356, upload-time = "2026-01-26T02:43:28.661Z" }, { url = "https://files.pythonhosted.org/packages/fd/d2/0a36c8473f0cbaeadd5db6c8b72d15bbceeec275807772bfcd059bef487d/multidict-6.7.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8be1802715a8e892c784c0197c2ace276ea52702a0ede98b6310c8f255a5afb3", size = 244355, upload-time = "2026-01-26T02:43:31.165Z" }, { url = "https://files.pythonhosted.org/packages/5d/16/8c65be997fd7dd311b7d39c7b6e71a0cb449bad093761481eccbbe4b42a2/multidict-6.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e2d2ed645ea29f31c4c7ea1552fcfd7cb7ba656e1eafd4134a6620c9f5fdd9e", size = 246433, upload-time = "2026-01-26T02:43:32.581Z" }, { url = "https://files.pythonhosted.org/packages/01/fb/4dbd7e848d2799c6a026ec88ad39cf2b8416aa167fcc903baa55ecaa045c/multidict-6.7.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:95922cee9a778659e91db6497596435777bd25ed116701a4c034f8e46544955a", size = 225376, upload-time = "2026-01-26T02:43:34.417Z" }, { url = "https://files.pythonhosted.org/packages/b6/8a/4a3a6341eac3830f6053062f8fbc9a9e54407c80755b3f05bc427295c2d0/multidict-6.7.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6b83cabdc375ffaaa15edd97eb7c0c672ad788e2687004990074d7d6c9b140c8", size = 257365, upload-time = "2026-01-26T02:43:35.741Z" }, { url = "https://files.pythonhosted.org/packages/f7/a2/dd575a69c1aa206e12d27d0770cdf9b92434b48a9ef0cd0d1afdecaa93c4/multidict-6.7.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:38fb49540705369bab8484db0689d86c0a33a0a9f2c1b197f506b71b4b6c19b0", size = 254747, upload-time = "2026-01-26T02:43:36.976Z" }, { url = "https://files.pythonhosted.org/packages/5a/56/21b27c560c13822ed93133f08aa6372c53a8e067f11fbed37b4adcdac922/multidict-6.7.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:439cbebd499f92e9aa6793016a8acaa161dfa749ae86d20960189f5398a19144", size = 246293, upload-time = "2026-01-26T02:43:38.258Z" }, { url = "https://files.pythonhosted.org/packages/5a/a4/23466059dc3854763423d0ad6c0f3683a379d97673b1b89ec33826e46728/multidict-6.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6d3bc717b6fe763b8be3f2bee2701d3c8eb1b2a8ae9f60910f1b2860c82b6c49", size = 242962, upload-time = "2026-01-26T02:43:40.034Z" }, { url = "https://files.pythonhosted.org/packages/1f/67/51dd754a3524d685958001e8fa20a0f5f90a6a856e0a9dcabff69be3dbb7/multidict-6.7.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:619e5a1ac57986dbfec9f0b301d865dddf763696435e2962f6d9cf2fdff2bb71", size = 237360, upload-time = "2026-01-26T02:43:41.752Z" }, { url = "https://files.pythonhosted.org/packages/64/3f/036dfc8c174934d4b55d86ff4f978e558b0e585cef70cfc1ad01adc6bf18/multidict-6.7.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0b38ebffd9be37c1170d33bc0f36f4f262e0a09bc1aac1c34c7aa51a7293f0b3", size = 245940, upload-time = "2026-01-26T02:43:43.042Z" }, { url = "https://files.pythonhosted.org/packages/3d/20/6214d3c105928ebc353a1c644a6ef1408bc5794fcb4f170bb524a3c16311/multidict-6.7.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:10ae39c9cfe6adedcdb764f5e8411d4a92b055e35573a2eaa88d3323289ef93c", size = 253502, upload-time = "2026-01-26T02:43:44.371Z" }, { url = "https://files.pythonhosted.org/packages/b1/e2/c653bc4ae1be70a0f836b82172d643fcf1dade042ba2676ab08ec08bff0f/multidict-6.7.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:25167cc263257660290fba06b9318d2026e3c910be240a146e1f66dd114af2b0", size = 247065, upload-time = "2026-01-26T02:43:45.745Z" }, { url = "https://files.pythonhosted.org/packages/c8/11/a854b4154cd3bd8b1fd375e8a8ca9d73be37610c361543d56f764109509b/multidict-6.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:128441d052254f42989ef98b7b6a6ecb1e6f708aa962c7984235316db59f50fa", size = 241870, upload-time = "2026-01-26T02:43:47.054Z" }, { url = "https://files.pythonhosted.org/packages/13/bf/9676c0392309b5fdae322333d22a829715b570edb9baa8016a517b55b558/multidict-6.7.1-cp311-cp311-win32.whl", hash = "sha256:d62b7f64ffde3b99d06b707a280db04fb3855b55f5a06df387236051d0668f4a", size = 41302, upload-time = "2026-01-26T02:43:48.753Z" }, { url = "https://files.pythonhosted.org/packages/c9/68/f16a3a8ba6f7b6dc92a1f19669c0810bd2c43fc5a02da13b1cbf8e253845/multidict-6.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:bdbf9f3b332abd0cdb306e7c2113818ab1e922dc84b8f8fd06ec89ed2a19ab8b", size = 45981, upload-time = "2026-01-26T02:43:49.921Z" }, { url = "https://files.pythonhosted.org/packages/ac/ad/9dd5305253fa00cd3c7555dbef69d5bf4133debc53b87ab8d6a44d411665/multidict-6.7.1-cp311-cp311-win_arm64.whl", hash = "sha256:b8c990b037d2fff2f4e33d3f21b9b531c5745b33a49a7d6dbe7a177266af44f6", size = 43159, upload-time = "2026-01-26T02:43:51.635Z" }, { url = "https://files.pythonhosted.org/packages/8d/9c/f20e0e2cf80e4b2e4b1c365bf5fe104ee633c751a724246262db8f1a0b13/multidict-6.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a90f75c956e32891a4eda3639ce6dd86e87105271f43d43442a3aedf3cddf172", size = 76893, upload-time = "2026-01-26T02:43:52.754Z" }, { url = "https://files.pythonhosted.org/packages/fe/cf/18ef143a81610136d3da8193da9d80bfe1cb548a1e2d1c775f26b23d024a/multidict-6.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fccb473e87eaa1382689053e4a4618e7ba7b9b9b8d6adf2027ee474597128cd", size = 45456, upload-time = "2026-01-26T02:43:53.893Z" }, { url = "https://files.pythonhosted.org/packages/a9/65/1caac9d4cd32e8433908683446eebc953e82d22b03d10d41a5f0fefe991b/multidict-6.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0fa96985700739c4c7853a43c0b3e169360d6855780021bfc6d0f1ce7c123e7", size = 43872, upload-time = "2026-01-26T02:43:55.041Z" }, { url = "https://files.pythonhosted.org/packages/cf/3b/d6bd75dc4f3ff7c73766e04e705b00ed6dbbaccf670d9e05a12b006f5a21/multidict-6.7.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb2a55f408c3043e42b40cc8eecd575afa27b7e0b956dfb190de0f8499a57a53", size = 251018, upload-time = "2026-01-26T02:43:56.198Z" }, { url = "https://files.pythonhosted.org/packages/fd/80/c959c5933adedb9ac15152e4067c702a808ea183a8b64cf8f31af8ad3155/multidict-6.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb0ce7b2a32d09892b3dd6cc44877a0d02a33241fafca5f25c8b6b62374f8b75", size = 258883, upload-time = "2026-01-26T02:43:57.499Z" }, { url = "https://files.pythonhosted.org/packages/86/85/7ed40adafea3d4f1c8b916e3b5cc3a8e07dfcdcb9cd72800f4ed3ca1b387/multidict-6.7.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c3a32d23520ee37bf327d1e1a656fec76a2edd5c038bf43eddfa0572ec49c60b", size = 242413, upload-time = "2026-01-26T02:43:58.755Z" }, { url = "https://files.pythonhosted.org/packages/d2/57/b8565ff533e48595503c785f8361ff9a4fde4d67de25c207cd0ba3befd03/multidict-6.7.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9c90fed18bffc0189ba814749fdcc102b536e83a9f738a9003e569acd540a733", size = 268404, upload-time = "2026-01-26T02:44:00.216Z" }, { url = "https://files.pythonhosted.org/packages/e0/50/9810c5c29350f7258180dfdcb2e52783a0632862eb334c4896ac717cebcb/multidict-6.7.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:da62917e6076f512daccfbbde27f46fed1c98fee202f0559adec8ee0de67f71a", size = 269456, upload-time = "2026-01-26T02:44:02.202Z" }, { url = "https://files.pythonhosted.org/packages/f3/8d/5e5be3ced1d12966fefb5c4ea3b2a5b480afcea36406559442c6e31d4a48/multidict-6.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde23ef6ed9db7eaee6c37dcec08524cb43903c60b285b172b6c094711b3961", size = 256322, upload-time = "2026-01-26T02:44:03.56Z" }, { url = "https://files.pythonhosted.org/packages/31/6e/d8a26d81ac166a5592782d208dd90dfdc0a7a218adaa52b45a672b46c122/multidict-6.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3758692429e4e32f1ba0df23219cd0b4fc0a52f476726fff9337d1a57676a582", size = 253955, upload-time = "2026-01-26T02:44:04.845Z" }, { url = "https://files.pythonhosted.org/packages/59/4c/7c672c8aad41534ba619bcd4ade7a0dc87ed6b8b5c06149b85d3dd03f0cd/multidict-6.7.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:398c1478926eca669f2fd6a5856b6de9c0acf23a2cb59a14c0ba5844fa38077e", size = 251254, upload-time = "2026-01-26T02:44:06.133Z" }, { url = "https://files.pythonhosted.org/packages/7b/bd/84c24de512cbafbdbc39439f74e967f19570ce7924e3007174a29c348916/multidict-6.7.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c102791b1c4f3ab36ce4101154549105a53dc828f016356b3e3bcae2e3a039d3", size = 252059, upload-time = "2026-01-26T02:44:07.518Z" }, { url = "https://files.pythonhosted.org/packages/fa/ba/f5449385510825b73d01c2d4087bf6d2fccc20a2d42ac34df93191d3dd03/multidict-6.7.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a088b62bd733e2ad12c50dad01b7d0166c30287c166e137433d3b410add807a6", size = 263588, upload-time = "2026-01-26T02:44:09.382Z" }, { url = "https://files.pythonhosted.org/packages/d7/11/afc7c677f68f75c84a69fe37184f0f82fce13ce4b92f49f3db280b7e92b3/multidict-6.7.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3d51ff4785d58d3f6c91bdbffcb5e1f7ddfda557727043aa20d20ec4f65e324a", size = 259642, upload-time = "2026-01-26T02:44:10.73Z" }, { url = "https://files.pythonhosted.org/packages/2b/17/ebb9644da78c4ab36403739e0e6e0e30ebb135b9caf3440825001a0bddcb/multidict-6.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc5907494fccf3e7d3f94f95c91d6336b092b5fc83811720fae5e2765890dfba", size = 251377, upload-time = "2026-01-26T02:44:12.042Z" }, { url = "https://files.pythonhosted.org/packages/ca/a4/840f5b97339e27846c46307f2530a2805d9d537d8b8bd416af031cad7fa0/multidict-6.7.1-cp312-cp312-win32.whl", hash = "sha256:28ca5ce2fd9716631133d0e9a9b9a745ad7f60bac2bccafb56aa380fc0b6c511", size = 41887, upload-time = "2026-01-26T02:44:14.245Z" }, { url = "https://files.pythonhosted.org/packages/80/31/0b2517913687895f5904325c2069d6a3b78f66cc641a86a2baf75a05dcbb/multidict-6.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcee94dfbd638784645b066074b338bc9cc155d4b4bffa4adce1615c5a426c19", size = 46053, upload-time = "2026-01-26T02:44:15.371Z" }, { url = "https://files.pythonhosted.org/packages/0c/5b/aba28e4ee4006ae4c7df8d327d31025d760ffa992ea23812a601d226e682/multidict-6.7.1-cp312-cp312-win_arm64.whl", hash = "sha256:ba0a9fb644d0c1a2194cf7ffb043bd852cea63a57f66fbd33959f7dae18517bf", size = 43307, upload-time = "2026-01-26T02:44:16.852Z" }, { url = "https://files.pythonhosted.org/packages/f2/22/929c141d6c0dba87d3e1d38fbdf1ba8baba86b7776469f2bc2d3227a1e67/multidict-6.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23", size = 76174, upload-time = "2026-01-26T02:44:18.509Z" }, { url = "https://files.pythonhosted.org/packages/c7/75/bc704ae15fee974f8fccd871305e254754167dce5f9e42d88a2def741a1d/multidict-6.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2", size = 45116, upload-time = "2026-01-26T02:44:19.745Z" }, { url = "https://files.pythonhosted.org/packages/79/76/55cd7186f498ed080a18440c9013011eb548f77ae1b297206d030eb1180a/multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445", size = 43524, upload-time = "2026-01-26T02:44:21.571Z" }, { url = "https://files.pythonhosted.org/packages/e9/3c/414842ef8d5a1628d68edee29ba0e5bcf235dbfb3ccd3ea303a7fe8c72ff/multidict-6.7.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177", size = 249368, upload-time = "2026-01-26T02:44:22.803Z" }, { url = "https://files.pythonhosted.org/packages/f6/32/befed7f74c458b4a525e60519fe8d87eef72bb1e99924fa2b0f9d97a221e/multidict-6.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23", size = 256952, upload-time = "2026-01-26T02:44:24.306Z" }, { url = "https://files.pythonhosted.org/packages/03/d6/c878a44ba877f366630c860fdf74bfb203c33778f12b6ac274936853c451/multidict-6.7.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060", size = 240317, upload-time = "2026-01-26T02:44:25.772Z" }, { url = "https://files.pythonhosted.org/packages/68/49/57421b4d7ad2e9e60e25922b08ceb37e077b90444bde6ead629095327a6f/multidict-6.7.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d", size = 267132, upload-time = "2026-01-26T02:44:27.648Z" }, { url = "https://files.pythonhosted.org/packages/b7/fe/ec0edd52ddbcea2a2e89e174f0206444a61440b40f39704e64dc807a70bd/multidict-6.7.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed", size = 268140, upload-time = "2026-01-26T02:44:29.588Z" }, { url = "https://files.pythonhosted.org/packages/b0/73/6e1b01cbeb458807aa0831742232dbdd1fa92bfa33f52a3f176b4ff3dc11/multidict-6.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429", size = 254277, upload-time = "2026-01-26T02:44:30.902Z" }, { url = "https://files.pythonhosted.org/packages/6a/b2/5fb8c124d7561a4974c342bc8c778b471ebbeb3cc17df696f034a7e9afe7/multidict-6.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6", size = 252291, upload-time = "2026-01-26T02:44:32.31Z" }, { url = "https://files.pythonhosted.org/packages/5a/96/51d4e4e06bcce92577fcd488e22600bd38e4fd59c20cb49434d054903bd2/multidict-6.7.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9", size = 250156, upload-time = "2026-01-26T02:44:33.734Z" }, { url = "https://files.pythonhosted.org/packages/db/6b/420e173eec5fba721a50e2a9f89eda89d9c98fded1124f8d5c675f7a0c0f/multidict-6.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c", size = 249742, upload-time = "2026-01-26T02:44:35.222Z" }, { url = "https://files.pythonhosted.org/packages/44/a3/ec5b5bd98f306bc2aa297b8c6f11a46714a56b1e6ef5ebda50a4f5d7c5fb/multidict-6.7.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84", size = 262221, upload-time = "2026-01-26T02:44:36.604Z" }, { url = "https://files.pythonhosted.org/packages/cd/f7/e8c0d0da0cd1e28d10e624604e1a36bcc3353aaebdfdc3a43c72bc683a12/multidict-6.7.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d", size = 258664, upload-time = "2026-01-26T02:44:38.008Z" }, { url = "https://files.pythonhosted.org/packages/52/da/151a44e8016dd33feed44f730bd856a66257c1ee7aed4f44b649fb7edeb3/multidict-6.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33", size = 249490, upload-time = "2026-01-26T02:44:39.386Z" }, { url = "https://files.pythonhosted.org/packages/87/af/a3b86bf9630b732897f6fc3f4c4714b90aa4361983ccbdcd6c0339b21b0c/multidict-6.7.1-cp313-cp313-win32.whl", hash = "sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3", size = 41695, upload-time = "2026-01-26T02:44:41.318Z" }, { url = "https://files.pythonhosted.org/packages/b2/35/e994121b0e90e46134673422dd564623f93304614f5d11886b1b3e06f503/multidict-6.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5", size = 45884, upload-time = "2026-01-26T02:44:42.488Z" }, { url = "https://files.pythonhosted.org/packages/ca/61/42d3e5dbf661242a69c97ea363f2d7b46c567da8eadef8890022be6e2ab0/multidict-6.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df", size = 43122, upload-time = "2026-01-26T02:44:43.664Z" }, { url = "https://files.pythonhosted.org/packages/6d/b3/e6b21c6c4f314bb956016b0b3ef2162590a529b84cb831c257519e7fde44/multidict-6.7.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1", size = 83175, upload-time = "2026-01-26T02:44:44.894Z" }, { url = "https://files.pythonhosted.org/packages/fb/76/23ecd2abfe0957b234f6c960f4ade497f55f2c16aeb684d4ecdbf1c95791/multidict-6.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963", size = 48460, upload-time = "2026-01-26T02:44:46.106Z" }, { url = "https://files.pythonhosted.org/packages/c4/57/a0ed92b23f3a042c36bc4227b72b97eca803f5f1801c1ab77c8a212d455e/multidict-6.7.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34", size = 46930, upload-time = "2026-01-26T02:44:47.278Z" }, { url = "https://files.pythonhosted.org/packages/b5/66/02ec7ace29162e447f6382c495dc95826bf931d3818799bbef11e8f7df1a/multidict-6.7.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65", size = 242582, upload-time = "2026-01-26T02:44:48.604Z" }, { url = "https://files.pythonhosted.org/packages/58/18/64f5a795e7677670e872673aca234162514696274597b3708b2c0d276cce/multidict-6.7.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292", size = 250031, upload-time = "2026-01-26T02:44:50.544Z" }, { url = "https://files.pythonhosted.org/packages/c8/ed/e192291dbbe51a8290c5686f482084d31bcd9d09af24f63358c3d42fd284/multidict-6.7.1-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43", size = 228596, upload-time = "2026-01-26T02:44:51.951Z" }, { url = "https://files.pythonhosted.org/packages/1e/7e/3562a15a60cf747397e7f2180b0a11dc0c38d9175a650e75fa1b4d325e15/multidict-6.7.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca", size = 257492, upload-time = "2026-01-26T02:44:53.902Z" }, { url = "https://files.pythonhosted.org/packages/24/02/7d0f9eae92b5249bb50ac1595b295f10e263dd0078ebb55115c31e0eaccd/multidict-6.7.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd", size = 255899, upload-time = "2026-01-26T02:44:55.316Z" }, { url = "https://files.pythonhosted.org/packages/00/e3/9b60ed9e23e64c73a5cde95269ef1330678e9c6e34dd4eb6b431b85b5a10/multidict-6.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7", size = 247970, upload-time = "2026-01-26T02:44:56.783Z" }, { url = "https://files.pythonhosted.org/packages/3e/06/538e58a63ed5cfb0bd4517e346b91da32fde409d839720f664e9a4ae4f9d/multidict-6.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3", size = 245060, upload-time = "2026-01-26T02:44:58.195Z" }, { url = "https://files.pythonhosted.org/packages/b2/2f/d743a3045a97c895d401e9bd29aaa09b94f5cbdf1bd561609e5a6c431c70/multidict-6.7.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4", size = 235888, upload-time = "2026-01-26T02:44:59.57Z" }, { url = "https://files.pythonhosted.org/packages/38/83/5a325cac191ab28b63c52f14f1131f3b0a55ba3b9aa65a6d0bf2a9b921a0/multidict-6.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8", size = 243554, upload-time = "2026-01-26T02:45:01.054Z" }, { url = "https://files.pythonhosted.org/packages/20/1f/9d2327086bd15da2725ef6aae624208e2ef828ed99892b17f60c344e57ed/multidict-6.7.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c", size = 252341, upload-time = "2026-01-26T02:45:02.484Z" }, { url = "https://files.pythonhosted.org/packages/e8/2c/2a1aa0280cf579d0f6eed8ee5211c4f1730bd7e06c636ba2ee6aafda302e/multidict-6.7.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52", size = 246391, upload-time = "2026-01-26T02:45:03.862Z" }, { url = "https://files.pythonhosted.org/packages/e5/03/7ca022ffc36c5a3f6e03b179a5ceb829be9da5783e6fe395f347c0794680/multidict-6.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108", size = 243422, upload-time = "2026-01-26T02:45:05.296Z" }, { url = "https://files.pythonhosted.org/packages/dc/1d/b31650eab6c5778aceed46ba735bd97f7c7d2f54b319fa916c0f96e7805b/multidict-6.7.1-cp313-cp313t-win32.whl", hash = "sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32", size = 47770, upload-time = "2026-01-26T02:45:06.754Z" }, { url = "https://files.pythonhosted.org/packages/ac/5b/2d2d1d522e51285bd61b1e20df8f47ae1a9d80839db0b24ea783b3832832/multidict-6.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8", size = 53109, upload-time = "2026-01-26T02:45:08.044Z" }, { url = "https://files.pythonhosted.org/packages/3d/a3/cc409ba012c83ca024a308516703cf339bdc4b696195644a7215a5164a24/multidict-6.7.1-cp313-cp313t-win_arm64.whl", hash = "sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118", size = 45573, upload-time = "2026-01-26T02:45:09.349Z" }, { url = "https://files.pythonhosted.org/packages/91/cc/db74228a8be41884a567e88a62fd589a913708fcf180d029898c17a9a371/multidict-6.7.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8f333ec9c5eb1b7105e3b84b53141e66ca05a19a605368c55450b6ba208cb9ee", size = 75190, upload-time = "2026-01-26T02:45:10.651Z" }, { url = "https://files.pythonhosted.org/packages/d5/22/492f2246bb5b534abd44804292e81eeaf835388901f0c574bac4eeec73c5/multidict-6.7.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a407f13c188f804c759fc6a9f88286a565c242a76b27626594c133b82883b5c2", size = 44486, upload-time = "2026-01-26T02:45:11.938Z" }, { url = "https://files.pythonhosted.org/packages/f1/4f/733c48f270565d78b4544f2baddc2fb2a245e5a8640254b12c36ac7ac68e/multidict-6.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0e161ddf326db5577c3a4cc2d8648f81456e8a20d40415541587a71620d7a7d1", size = 43219, upload-time = "2026-01-26T02:45:14.346Z" }, { url = "https://files.pythonhosted.org/packages/24/bb/2c0c2287963f4259c85e8bcbba9182ced8d7fca65c780c38e99e61629d11/multidict-6.7.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1e3a8bb24342a8201d178c3b4984c26ba81a577c80d4d525727427460a50c22d", size = 245132, upload-time = "2026-01-26T02:45:15.712Z" }, { url = "https://files.pythonhosted.org/packages/a7/f9/44d4b3064c65079d2467888794dea218d1601898ac50222ab8a9a8094460/multidict-6.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97231140a50f5d447d3164f994b86a0bed7cd016e2682f8650d6a9158e14fd31", size = 252420, upload-time = "2026-01-26T02:45:17.293Z" }, { url = "https://files.pythonhosted.org/packages/8b/13/78f7275e73fa17b24c9a51b0bd9d73ba64bb32d0ed51b02a746eb876abe7/multidict-6.7.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b10359683bd8806a200fd2909e7c8ca3a7b24ec1d8132e483d58e791d881048", size = 233510, upload-time = "2026-01-26T02:45:19.356Z" }, { url = "https://files.pythonhosted.org/packages/4b/25/8167187f62ae3cbd52da7893f58cb036b47ea3fb67138787c76800158982/multidict-6.7.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:283ddac99f7ac25a4acadbf004cb5ae34480bbeb063520f70ce397b281859362", size = 264094, upload-time = "2026-01-26T02:45:20.834Z" }, { url = "https://files.pythonhosted.org/packages/a1/e7/69a3a83b7b030cf283fb06ce074a05a02322359783424d7edf0f15fe5022/multidict-6.7.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:538cec1e18c067d0e6103aa9a74f9e832904c957adc260e61cd9d8cf0c3b3d37", size = 260786, upload-time = "2026-01-26T02:45:22.818Z" }, { url = "https://files.pythonhosted.org/packages/fe/3b/8ec5074bcfc450fe84273713b4b0a0dd47c0249358f5d82eb8104ffe2520/multidict-6.7.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eee46ccb30ff48a1e35bb818cc90846c6be2b68240e42a78599166722cea709", size = 248483, upload-time = "2026-01-26T02:45:24.368Z" }, { url = "https://files.pythonhosted.org/packages/48/5a/d5a99e3acbca0e29c5d9cba8f92ceb15dce78bab963b308ae692981e3a5d/multidict-6.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa263a02f4f2dd2d11a7b1bb4362aa7cb1049f84a9235d31adf63f30143469a0", size = 248403, upload-time = "2026-01-26T02:45:25.982Z" }, { url = "https://files.pythonhosted.org/packages/35/48/e58cd31f6c7d5102f2a4bf89f96b9cf7e00b6c6f3d04ecc44417c00a5a3c/multidict-6.7.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2e1425e2f99ec5bd36c15a01b690a1a2456209c5deed58f95469ffb46039ccbb", size = 240315, upload-time = "2026-01-26T02:45:27.487Z" }, { url = "https://files.pythonhosted.org/packages/94/33/1cd210229559cb90b6786c30676bb0c58249ff42f942765f88793b41fdce/multidict-6.7.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:497394b3239fc6f0e13a78a3e1b61296e72bf1c5f94b4c4eb80b265c37a131cd", size = 245528, upload-time = "2026-01-26T02:45:28.991Z" }, { url = "https://files.pythonhosted.org/packages/64/f2/6e1107d226278c876c783056b7db43d800bb64c6131cec9c8dfb6903698e/multidict-6.7.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:233b398c29d3f1b9676b4b6f75c518a06fcb2ea0b925119fb2c1bc35c05e1601", size = 258784, upload-time = "2026-01-26T02:45:30.503Z" }, { url = "https://files.pythonhosted.org/packages/4d/c1/11f664f14d525e4a1b5327a82d4de61a1db604ab34c6603bb3c2cc63ad34/multidict-6.7.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:93b1818e4a6e0930454f0f2af7dfce69307ca03cdcfb3739bf4d91241967b6c1", size = 251980, upload-time = "2026-01-26T02:45:32.603Z" }, { url = "https://files.pythonhosted.org/packages/e1/9f/75a9ac888121d0c5bbd4ecf4eead45668b1766f6baabfb3b7f66a410e231/multidict-6.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f33dc2a3abe9249ea5d8360f969ec7f4142e7ac45ee7014d8f8d5acddf178b7b", size = 243602, upload-time = "2026-01-26T02:45:34.043Z" }, { url = "https://files.pythonhosted.org/packages/9a/e7/50bf7b004cc8525d80dbbbedfdc7aed3e4c323810890be4413e589074032/multidict-6.7.1-cp314-cp314-win32.whl", hash = "sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d", size = 40930, upload-time = "2026-01-26T02:45:36.278Z" }, { url = "https://files.pythonhosted.org/packages/e0/bf/52f25716bbe93745595800f36fb17b73711f14da59ed0bb2eba141bc9f0f/multidict-6.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f", size = 45074, upload-time = "2026-01-26T02:45:37.546Z" }, { url = "https://files.pythonhosted.org/packages/97/ab/22803b03285fa3a525f48217963da3a65ae40f6a1b6f6cf2768879e208f9/multidict-6.7.1-cp314-cp314-win_arm64.whl", hash = "sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5", size = 42471, upload-time = "2026-01-26T02:45:38.889Z" }, { url = "https://files.pythonhosted.org/packages/e0/6d/f9293baa6146ba9507e360ea0292b6422b016907c393e2f63fc40ab7b7b5/multidict-6.7.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0458c978acd8e6ea53c81eefaddbbee9c6c5e591f41b3f5e8e194780fe026581", size = 82401, upload-time = "2026-01-26T02:45:40.254Z" }, { url = "https://files.pythonhosted.org/packages/7a/68/53b5494738d83558d87c3c71a486504d8373421c3e0dbb6d0db48ad42ee0/multidict-6.7.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c0abd12629b0af3cf590982c0b413b1e7395cd4ec026f30986818ab95bfaa94a", size = 48143, upload-time = "2026-01-26T02:45:41.635Z" }, { url = "https://files.pythonhosted.org/packages/37/e8/5284c53310dcdc99ce5d66563f6e5773531a9b9fe9ec7a615e9bc306b05f/multidict-6.7.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:14525a5f61d7d0c94b368a42cff4c9a4e7ba2d52e2672a7b23d84dc86fb02b0c", size = 46507, upload-time = "2026-01-26T02:45:42.99Z" }, { url = "https://files.pythonhosted.org/packages/e4/fc/6800d0e5b3875568b4083ecf5f310dcf91d86d52573160834fb4bfcf5e4f/multidict-6.7.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17307b22c217b4cf05033dabefe68255a534d637c6c9b0cc8382718f87be4262", size = 239358, upload-time = "2026-01-26T02:45:44.376Z" }, { url = "https://files.pythonhosted.org/packages/41/75/4ad0973179361cdf3a113905e6e088173198349131be2b390f9fa4da5fc6/multidict-6.7.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a7e590ff876a3eaf1c02a4dfe0724b6e69a9e9de6d8f556816f29c496046e59", size = 246884, upload-time = "2026-01-26T02:45:47.167Z" }, { url = "https://files.pythonhosted.org/packages/c3/9c/095bb28b5da139bd41fb9a5d5caff412584f377914bd8787c2aa98717130/multidict-6.7.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5fa6a95dfee63893d80a34758cd0e0c118a30b8dcb46372bf75106c591b77889", size = 225878, upload-time = "2026-01-26T02:45:48.698Z" }, { url = "https://files.pythonhosted.org/packages/07/d0/c0a72000243756e8f5a277b6b514fa005f2c73d481b7d9e47cd4568aa2e4/multidict-6.7.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a0543217a6a017692aa6ae5cc39adb75e587af0f3a82288b1492eb73dd6cc2a4", size = 253542, upload-time = "2026-01-26T02:45:50.164Z" }, { url = "https://files.pythonhosted.org/packages/c0/6b/f69da15289e384ecf2a68837ec8b5ad8c33e973aa18b266f50fe55f24b8c/multidict-6.7.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f99fe611c312b3c1c0ace793f92464d8cd263cc3b26b5721950d977b006b6c4d", size = 252403, upload-time = "2026-01-26T02:45:51.779Z" }, { url = "https://files.pythonhosted.org/packages/a2/76/b9669547afa5a1a25cd93eaca91c0da1c095b06b6d2d8ec25b713588d3a1/multidict-6.7.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9004d8386d133b7e6135679424c91b0b854d2d164af6ea3f289f8f2761064609", size = 244889, upload-time = "2026-01-26T02:45:53.27Z" }, { url = "https://files.pythonhosted.org/packages/7e/a9/a50d2669e506dad33cfc45b5d574a205587b7b8a5f426f2fbb2e90882588/multidict-6.7.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e628ef0e6859ffd8273c69412a2465c4be4a9517d07261b33334b5ec6f3c7489", size = 241982, upload-time = "2026-01-26T02:45:54.919Z" }, { url = "https://files.pythonhosted.org/packages/c5/bb/1609558ad8b456b4827d3c5a5b775c93b87878fd3117ed3db3423dfbce1b/multidict-6.7.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:841189848ba629c3552035a6a7f5bf3b02eb304e9fea7492ca220a8eda6b0e5c", size = 232415, upload-time = "2026-01-26T02:45:56.981Z" }, { url = "https://files.pythonhosted.org/packages/d8/59/6f61039d2aa9261871e03ab9dc058a550d240f25859b05b67fd70f80d4b3/multidict-6.7.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce1bbd7d780bb5a0da032e095c951f7014d6b0a205f8318308140f1a6aba159e", size = 240337, upload-time = "2026-01-26T02:45:58.698Z" }, { url = "https://files.pythonhosted.org/packages/a1/29/fdc6a43c203890dc2ae9249971ecd0c41deaedfe00d25cb6564b2edd99eb/multidict-6.7.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b26684587228afed0d50cf804cc71062cc9c1cdf55051c4c6345d372947b268c", size = 248788, upload-time = "2026-01-26T02:46:00.862Z" }, { url = "https://files.pythonhosted.org/packages/a9/14/a153a06101323e4cf086ecee3faadba52ff71633d471f9685c42e3736163/multidict-6.7.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9f9af11306994335398293f9958071019e3ab95e9a707dc1383a35613f6abcb9", size = 242842, upload-time = "2026-01-26T02:46:02.824Z" }, { url = "https://files.pythonhosted.org/packages/41/5f/604ae839e64a4a6efc80db94465348d3b328ee955e37acb24badbcd24d83/multidict-6.7.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b4938326284c4f1224178a560987b6cf8b4d38458b113d9b8c1db1a836e640a2", size = 240237, upload-time = "2026-01-26T02:46:05.898Z" }, { url = "https://files.pythonhosted.org/packages/5f/60/c3a5187bf66f6fb546ff4ab8fb5a077cbdd832d7b1908d4365c7f74a1917/multidict-6.7.1-cp314-cp314t-win32.whl", hash = "sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7", size = 48008, upload-time = "2026-01-26T02:46:07.468Z" }, { url = "https://files.pythonhosted.org/packages/0c/f7/addf1087b860ac60e6f382240f64fb99f8bfb532bb06f7c542b83c29ca61/multidict-6.7.1-cp314-cp314t-win_amd64.whl", hash = "sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5", size = 53542, upload-time = "2026-01-26T02:46:08.809Z" }, { url = "https://files.pythonhosted.org/packages/4c/81/4629d0aa32302ef7b2ec65c75a728cc5ff4fa410c50096174c1632e70b3e/multidict-6.7.1-cp314-cp314t-win_arm64.whl", hash = "sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2", size = 44719, upload-time = "2026-01-26T02:46:11.146Z" }, { url = "https://files.pythonhosted.org/packages/9e/ee/74525ebe3eb5fddcd6735fc03cbea3feeed4122b53bc798ac32d297ac9ae/multidict-6.7.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:65573858d27cdeaca41893185677dc82395159aa28875a8867af66532d413a8f", size = 77107, upload-time = "2026-01-26T02:46:12.608Z" }, { url = "https://files.pythonhosted.org/packages/f0/9a/ce8744e777a74b3050b1bf56be3eed1053b3457302ea055f1ea437200a23/multidict-6.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c524c6fb8fc342793708ab111c4dbc90ff9abd568de220432500e47e990c0358", size = 44943, upload-time = "2026-01-26T02:46:14.016Z" }, { url = "https://files.pythonhosted.org/packages/83/9c/1d2a283d9c6f31e260cb6c2fccadc3edcf6c4c14ee0929cd2af4d2606dd7/multidict-6.7.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:aa23b001d968faef416ff70dc0f1ab045517b9b42a90edd3e9bcdb06479e31d5", size = 44603, upload-time = "2026-01-26T02:46:15.391Z" }, { url = "https://files.pythonhosted.org/packages/87/9d/3b186201671583d8e8d6d79c07481a5aafd0ba7575e3d8566baec80c1e82/multidict-6.7.1-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6704fa2b7453b2fb121740555fa1ee20cd98c4d011120caf4d2b8d4e7c76eec0", size = 240573, upload-time = "2026-01-26T02:46:16.783Z" }, { url = "https://files.pythonhosted.org/packages/42/7d/a52f5d4d0754311d1ac78478e34dff88de71259a8585e05ee14e5f877caf/multidict-6.7.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:121a34e5bfa410cdf2c8c49716de160de3b1dbcd86b49656f5681e4543bcd1a8", size = 240106, upload-time = "2026-01-26T02:46:18.432Z" }, { url = "https://files.pythonhosted.org/packages/84/9f/d80118e6c30ff55b7d171bdc5520aad4b9626e657520b8d7c8ca8c2fad12/multidict-6.7.1-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:026d264228bcd637d4e060844e39cdc60f86c479e463d49075dedc21b18fbbe0", size = 219418, upload-time = "2026-01-26T02:46:20.526Z" }, { url = "https://files.pythonhosted.org/packages/c7/bd/896e60b3457f194de77c7de64f9acce9f75da0518a5230ce1df534f6747b/multidict-6.7.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0e697826df7eb63418ee190fd06ce9f1803593bb4b9517d08c60d9b9a7f69d8f", size = 252124, upload-time = "2026-01-26T02:46:22.157Z" }, { url = "https://files.pythonhosted.org/packages/f4/de/ba6b30447c36a37078d0ba604aa12c1a52887af0c355236ca6e0a9d5286f/multidict-6.7.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bb08271280173720e9fea9ede98e5231defcbad90f1624bea26f32ec8a956e2f", size = 249402, upload-time = "2026-01-26T02:46:23.718Z" }, { url = "https://files.pythonhosted.org/packages/c2/b2/50a383c96230e432895a2fd3bcfe1b65785899598259d871d5de6b93180c/multidict-6.7.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c6b3228e1d80af737b72925ce5fb4daf5a335e49cd7ab77ed7b9fdfbf58c526e", size = 240346, upload-time = "2026-01-26T02:46:25.393Z" }, { url = "https://files.pythonhosted.org/packages/89/37/16d391fd8da544b1489306e38a46785fa41dd0f0ef766837ed7d4676dde0/multidict-6.7.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3943debf0fbb57bdde5901695c11094a9a36723e5c03875f87718ee15ca2f4d2", size = 237010, upload-time = "2026-01-26T02:46:27.408Z" }, { url = "https://files.pythonhosted.org/packages/b0/24/3152ee026eda86d5d3e3685182911e6951af7a016579da931080ce6ac9ad/multidict-6.7.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:98c5787b0a0d9a41d9311eae44c3b76e6753def8d8870ab501320efe75a6a5f8", size = 232018, upload-time = "2026-01-26T02:46:29.941Z" }, { url = "https://files.pythonhosted.org/packages/9c/1f/48d3c27a72be7fd23a55d8847193c459959bf35a5bb5844530dab00b739b/multidict-6.7.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:08ccb2a6dc72009093ebe7f3f073e5ec5964cba9a706fa94b1a1484039b87941", size = 241498, upload-time = "2026-01-26T02:46:32.052Z" }, { url = "https://files.pythonhosted.org/packages/1a/45/413643ae2952d0decdf6c1250f86d08a43e143271441e81027e38d598bd7/multidict-6.7.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb351f72c26dc9abe338ca7294661aa22969ad8ffe7ef7d5541d19f368dc854a", size = 247957, upload-time = "2026-01-26T02:46:33.666Z" }, { url = "https://files.pythonhosted.org/packages/50/f8/f1d0ac23df15e0470776388bdb261506f63af1f81d28bacb5e262d6e12b6/multidict-6.7.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ac1c665bad8b5d762f5f85ebe4d94130c26965f11de70c708c75671297c776de", size = 241651, upload-time = "2026-01-26T02:46:35.7Z" }, { url = "https://files.pythonhosted.org/packages/2c/c9/1a2a18f383cf129add66b6c36b75c3911a7ba95cf26cb141482de085cc12/multidict-6.7.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fa6609d0364f4f6f58351b4659a1f3e0e898ba2a8c5cac04cb2c7bc556b0bc5", size = 236371, upload-time = "2026-01-26T02:46:37.37Z" }, { url = "https://files.pythonhosted.org/packages/bb/aa/77d87e3fca31325b87e0eb72d5fe9a7472dcb51391a42df7ac1f3842f6c0/multidict-6.7.1-cp39-cp39-win32.whl", hash = "sha256:6f77ce314a29263e67adadc7e7c1bc699fcb3a305059ab973d038f87caa42ed0", size = 41426, upload-time = "2026-01-26T02:46:39.026Z" }, { url = "https://files.pythonhosted.org/packages/e3/b3/e8863e6a2da15a9d7e98976ff402e871b7352c76566df6c18d0378e0d9cf/multidict-6.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:f537b55778cd3cbee430abe3131255d3a78202e0f9ea7ffc6ada893a4bcaeea4", size = 46180, upload-time = "2026-01-26T02:46:40.422Z" }, { url = "https://files.pythonhosted.org/packages/93/d3/dd4fa951ad5b5fa216bf30054d705683d13405eea7459833d78f31b74c9c/multidict-6.7.1-cp39-cp39-win_arm64.whl", hash = "sha256:749aa54f578f2e5f439538706a475aa844bfa8ef75854b1401e6e528e4937cf9", size = 43231, upload-time = "2026-01-26T02:46:41.945Z" }, { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, ] [[package]] name = "mypy" version = "1.17.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mypy-extensions" }, { name = "pathspec" }, { name = "tomli", marker = "python_full_version < '3.11' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1e/e3/034322d5a779685218ed69286c32faa505247f1f096251ef66c8fd203b08/mypy-1.17.0.tar.gz", hash = "sha256:e5d7ccc08ba089c06e2f5629c660388ef1fee708444f1dee0b9203fa031dee03", size = 3352114, upload-time = "2025-07-14T20:34:30.181Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/6a/31/e762baa3b73905c856d45ab77b4af850e8159dffffd86a52879539a08c6b/mypy-1.17.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8e08de6138043108b3b18f09d3f817a4783912e48828ab397ecf183135d84d6", size = 10998313, upload-time = "2025-07-14T20:33:24.519Z" }, { url = "https://files.pythonhosted.org/packages/1c/c1/25b2f0d46fb7e0b5e2bee61ec3a47fe13eff9e3c2f2234f144858bbe6485/mypy-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce4a17920ec144647d448fc43725b5873548b1aae6c603225626747ededf582d", size = 10128922, upload-time = "2025-07-14T20:34:06.414Z" }, { url = "https://files.pythonhosted.org/packages/02/78/6d646603a57aa8a2886df1b8881fe777ea60f28098790c1089230cd9c61d/mypy-1.17.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ff25d151cc057fdddb1cb1881ef36e9c41fa2a5e78d8dd71bee6e4dcd2bc05b", size = 11913524, upload-time = "2025-07-14T20:33:19.109Z" }, { url = "https://files.pythonhosted.org/packages/4f/19/dae6c55e87ee426fb76980f7e78484450cad1c01c55a1dc4e91c930bea01/mypy-1.17.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93468cf29aa9a132bceb103bd8475f78cacde2b1b9a94fd978d50d4bdf616c9a", size = 12650527, upload-time = "2025-07-14T20:32:44.095Z" }, { url = "https://files.pythonhosted.org/packages/86/e1/f916845a235235a6c1e4d4d065a3930113767001d491b8b2e1b61ca56647/mypy-1.17.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:98189382b310f16343151f65dd7e6867386d3e35f7878c45cfa11383d175d91f", size = 12897284, upload-time = "2025-07-14T20:33:38.168Z" }, { url = "https://files.pythonhosted.org/packages/ae/dc/414760708a4ea1b096bd214d26a24e30ac5e917ef293bc33cdb6fe22d2da/mypy-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:c004135a300ab06a045c1c0d8e3f10215e71d7b4f5bb9a42ab80236364429937", size = 9506493, upload-time = "2025-07-14T20:34:01.093Z" }, { url = "https://files.pythonhosted.org/packages/d4/24/82efb502b0b0f661c49aa21cfe3e1999ddf64bf5500fc03b5a1536a39d39/mypy-1.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9d4fe5c72fd262d9c2c91c1117d16aac555e05f5beb2bae6a755274c6eec42be", size = 10914150, upload-time = "2025-07-14T20:31:51.985Z" }, { url = "https://files.pythonhosted.org/packages/03/96/8ef9a6ff8cedadff4400e2254689ca1dc4b420b92c55255b44573de10c54/mypy-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d96b196e5c16f41b4f7736840e8455958e832871990c7ba26bf58175e357ed61", size = 10039845, upload-time = "2025-07-14T20:32:30.527Z" }, { url = "https://files.pythonhosted.org/packages/df/32/7ce359a56be779d38021d07941cfbb099b41411d72d827230a36203dbb81/mypy-1.17.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:73a0ff2dd10337ceb521c080d4147755ee302dcde6e1a913babd59473904615f", size = 11837246, upload-time = "2025-07-14T20:32:01.28Z" }, { url = "https://files.pythonhosted.org/packages/82/16/b775047054de4d8dbd668df9137707e54b07fe18c7923839cd1e524bf756/mypy-1.17.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:24cfcc1179c4447854e9e406d3af0f77736d631ec87d31c6281ecd5025df625d", size = 12571106, upload-time = "2025-07-14T20:34:26.942Z" }, { url = "https://files.pythonhosted.org/packages/a1/cf/fa33eaf29a606102c8d9ffa45a386a04c2203d9ad18bf4eef3e20c43ebc8/mypy-1.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c56f180ff6430e6373db7a1d569317675b0a451caf5fef6ce4ab365f5f2f6c3", size = 12759960, upload-time = "2025-07-14T20:33:42.882Z" }, { url = "https://files.pythonhosted.org/packages/94/75/3f5a29209f27e739ca57e6350bc6b783a38c7621bdf9cac3ab8a08665801/mypy-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:eafaf8b9252734400f9b77df98b4eee3d2eecab16104680d51341c75702cad70", size = 9503888, upload-time = "2025-07-14T20:32:34.392Z" }, { url = "https://files.pythonhosted.org/packages/12/e9/e6824ed620bbf51d3bf4d6cbbe4953e83eaf31a448d1b3cfb3620ccb641c/mypy-1.17.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f986f1cab8dbec39ba6e0eaa42d4d3ac6686516a5d3dccd64be095db05ebc6bb", size = 11086395, upload-time = "2025-07-14T20:34:11.452Z" }, { url = "https://files.pythonhosted.org/packages/ba/51/a4afd1ae279707953be175d303f04a5a7bd7e28dc62463ad29c1c857927e/mypy-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:51e455a54d199dd6e931cd7ea987d061c2afbaf0960f7f66deef47c90d1b304d", size = 10120052, upload-time = "2025-07-14T20:33:09.897Z" }, { url = "https://files.pythonhosted.org/packages/8a/71/19adfeac926ba8205f1d1466d0d360d07b46486bf64360c54cb5a2bd86a8/mypy-1.17.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3204d773bab5ff4ebbd1f8efa11b498027cd57017c003ae970f310e5b96be8d8", size = 11861806, upload-time = "2025-07-14T20:32:16.028Z" }, { url = "https://files.pythonhosted.org/packages/0b/64/d6120eca3835baf7179e6797a0b61d6c47e0bc2324b1f6819d8428d5b9ba/mypy-1.17.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1051df7ec0886fa246a530ae917c473491e9a0ba6938cfd0ec2abc1076495c3e", size = 12744371, upload-time = "2025-07-14T20:33:33.503Z" }, { url = "https://files.pythonhosted.org/packages/1f/dc/56f53b5255a166f5bd0f137eed960e5065f2744509dfe69474ff0ba772a5/mypy-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f773c6d14dcc108a5b141b4456b0871df638eb411a89cd1c0c001fc4a9d08fc8", size = 12914558, upload-time = "2025-07-14T20:33:56.961Z" }, { url = "https://files.pythonhosted.org/packages/69/ac/070bad311171badc9add2910e7f89271695a25c136de24bbafc7eded56d5/mypy-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:1619a485fd0e9c959b943c7b519ed26b712de3002d7de43154a489a2d0fd817d", size = 9585447, upload-time = "2025-07-14T20:32:20.594Z" }, { url = "https://files.pythonhosted.org/packages/be/7b/5f8ab461369b9e62157072156935cec9d272196556bdc7c2ff5f4c7c0f9b/mypy-1.17.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c41aa59211e49d717d92b3bb1238c06d387c9325d3122085113c79118bebb06", size = 11070019, upload-time = "2025-07-14T20:32:07.99Z" }, { url = "https://files.pythonhosted.org/packages/9c/f8/c49c9e5a2ac0badcc54beb24e774d2499748302c9568f7f09e8730e953fa/mypy-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0e69db1fb65b3114f98c753e3930a00514f5b68794ba80590eb02090d54a5d4a", size = 10114457, upload-time = "2025-07-14T20:33:47.285Z" }, { url = "https://files.pythonhosted.org/packages/89/0c/fb3f9c939ad9beed3e328008b3fb90b20fda2cddc0f7e4c20dbefefc3b33/mypy-1.17.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:03ba330b76710f83d6ac500053f7727270b6b8553b0423348ffb3af6f2f7b889", size = 11857838, upload-time = "2025-07-14T20:33:14.462Z" }, { url = "https://files.pythonhosted.org/packages/4c/66/85607ab5137d65e4f54d9797b77d5a038ef34f714929cf8ad30b03f628df/mypy-1.17.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:037bc0f0b124ce46bfde955c647f3e395c6174476a968c0f22c95a8d2f589bba", size = 12731358, upload-time = "2025-07-14T20:32:25.579Z" }, { url = "https://files.pythonhosted.org/packages/73/d0/341dbbfb35ce53d01f8f2969facbb66486cee9804048bf6c01b048127501/mypy-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c38876106cb6132259683632b287238858bd58de267d80defb6f418e9ee50658", size = 12917480, upload-time = "2025-07-14T20:34:21.868Z" }, { url = "https://files.pythonhosted.org/packages/64/63/70c8b7dbfc520089ac48d01367a97e8acd734f65bd07813081f508a8c94c/mypy-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:d30ba01c0f151998f367506fab31c2ac4527e6a7b2690107c7a7f9e3cb419a9c", size = 9589666, upload-time = "2025-07-14T20:34:16.841Z" }, { url = "https://files.pythonhosted.org/packages/9f/a0/6263dd11941231f688f0a8f2faf90ceac1dc243d148d314a089d2fe25108/mypy-1.17.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:63e751f1b5ab51d6f3d219fe3a2fe4523eaa387d854ad06906c63883fde5b1ab", size = 10988185, upload-time = "2025-07-14T20:33:04.797Z" }, { url = "https://files.pythonhosted.org/packages/02/13/b8f16d6b0dc80277129559c8e7dbc9011241a0da8f60d031edb0e6e9ac8f/mypy-1.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f7fb09d05e0f1c329a36dcd30e27564a3555717cde87301fae4fb542402ddfad", size = 10120169, upload-time = "2025-07-14T20:32:38.84Z" }, { url = "https://files.pythonhosted.org/packages/14/ef/978ba79df0d65af680e20d43121363cf643eb79b04bf3880d01fc8afeb6f/mypy-1.17.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b72c34ce05ac3a1361ae2ebb50757fb6e3624032d91488d93544e9f82db0ed6c", size = 11918121, upload-time = "2025-07-14T20:33:52.328Z" }, { url = "https://files.pythonhosted.org/packages/f4/10/55ef70b104151a0d8280474f05268ff0a2a79be8d788d5e647257d121309/mypy-1.17.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:434ad499ad8dde8b2f6391ddfa982f41cb07ccda8e3c67781b1bfd4e5f9450a8", size = 12648821, upload-time = "2025-07-14T20:32:59.631Z" }, { url = "https://files.pythonhosted.org/packages/26/8c/7781fcd2e1eef48fbedd3a422c21fe300a8e03ed5be2eb4bd10246a77f4e/mypy-1.17.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f105f61a5eff52e137fd73bee32958b2add9d9f0a856f17314018646af838e97", size = 12896955, upload-time = "2025-07-14T20:32:49.543Z" }, { url = "https://files.pythonhosted.org/packages/78/13/03ac759dabe86e98ca7b6681f114f90ee03f3ff8365a57049d311bd4a4e3/mypy-1.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:ba06254a5a22729853209550d80f94e28690d5530c661f9416a68ac097b13fc4", size = 9512957, upload-time = "2025-07-14T20:33:28.619Z" }, { url = "https://files.pythonhosted.org/packages/e3/fc/ee058cc4316f219078464555873e99d170bde1d9569abd833300dbeb484a/mypy-1.17.0-py3-none-any.whl", hash = "sha256:15d9d0018237ab058e5de3d8fce61b6fa72cc59cc78fd91f1b474bce12abf496", size = 2283195, upload-time = "2025-07-14T20:31:54.753Z" }, ] [[package]] name = "mypy-extensions" version = "1.1.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, ] [[package]] name = "nodeenv" version = "1.10.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, ] [[package]] name = "packaging" version = "26.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, ] [[package]] name = "pathspec" version = "1.0.4" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, ] [[package]] name = "pluggy" version = "1.6.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, ] [[package]] name = "propcache" version = "0.4.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/3c/0e/934b541323035566a9af292dba85a195f7b78179114f2c6ebb24551118a9/propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db", size = 79534, upload-time = "2025-10-08T19:46:02.083Z" }, { url = "https://files.pythonhosted.org/packages/a1/6b/db0d03d96726d995dc7171286c6ba9d8d14251f37433890f88368951a44e/propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8", size = 45526, upload-time = "2025-10-08T19:46:03.884Z" }, { url = "https://files.pythonhosted.org/packages/e4/c3/82728404aea669e1600f304f2609cde9e665c18df5a11cdd57ed73c1dceb/propcache-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925", size = 47263, upload-time = "2025-10-08T19:46:05.405Z" }, { url = "https://files.pythonhosted.org/packages/df/1b/39313ddad2bf9187a1432654c38249bab4562ef535ef07f5eb6eb04d0b1b/propcache-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21", size = 201012, upload-time = "2025-10-08T19:46:07.165Z" }, { url = "https://files.pythonhosted.org/packages/5b/01/f1d0b57d136f294a142acf97f4ed58c8e5b974c21e543000968357115011/propcache-0.4.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5", size = 209491, upload-time = "2025-10-08T19:46:08.909Z" }, { url = "https://files.pythonhosted.org/packages/a1/c8/038d909c61c5bb039070b3fb02ad5cccdb1dde0d714792e251cdb17c9c05/propcache-0.4.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db", size = 215319, upload-time = "2025-10-08T19:46:10.7Z" }, { url = "https://files.pythonhosted.org/packages/08/57/8c87e93142b2c1fa2408e45695205a7ba05fb5db458c0bf5c06ba0e09ea6/propcache-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7", size = 196856, upload-time = "2025-10-08T19:46:12.003Z" }, { url = "https://files.pythonhosted.org/packages/42/df/5615fec76aa561987a534759b3686008a288e73107faa49a8ae5795a9f7a/propcache-0.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4", size = 193241, upload-time = "2025-10-08T19:46:13.495Z" }, { url = "https://files.pythonhosted.org/packages/d5/21/62949eb3a7a54afe8327011c90aca7e03547787a88fb8bd9726806482fea/propcache-0.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60", size = 190552, upload-time = "2025-10-08T19:46:14.938Z" }, { url = "https://files.pythonhosted.org/packages/30/ee/ab4d727dd70806e5b4de96a798ae7ac6e4d42516f030ee60522474b6b332/propcache-0.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f", size = 200113, upload-time = "2025-10-08T19:46:16.695Z" }, { url = "https://files.pythonhosted.org/packages/8a/0b/38b46208e6711b016aa8966a3ac793eee0d05c7159d8342aa27fc0bc365e/propcache-0.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900", size = 200778, upload-time = "2025-10-08T19:46:18.023Z" }, { url = "https://files.pythonhosted.org/packages/cf/81/5abec54355ed344476bee711e9f04815d4b00a311ab0535599204eecc257/propcache-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c", size = 193047, upload-time = "2025-10-08T19:46:19.449Z" }, { url = "https://files.pythonhosted.org/packages/ec/b6/1f237c04e32063cb034acd5f6ef34ef3a394f75502e72703545631ab1ef6/propcache-0.4.1-cp310-cp310-win32.whl", hash = "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb", size = 38093, upload-time = "2025-10-08T19:46:20.643Z" }, { url = "https://files.pythonhosted.org/packages/a6/67/354aac4e0603a15f76439caf0427781bcd6797f370377f75a642133bc954/propcache-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37", size = 41638, upload-time = "2025-10-08T19:46:21.935Z" }, { url = "https://files.pythonhosted.org/packages/e0/e1/74e55b9fd1a4c209ff1a9a824bf6c8b3d1fc5a1ac3eabe23462637466785/propcache-0.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581", size = 38229, upload-time = "2025-10-08T19:46:23.368Z" }, { url = "https://files.pythonhosted.org/packages/8c/d4/4e2c9aaf7ac2242b9358f98dccd8f90f2605402f5afeff6c578682c2c491/propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf", size = 80208, upload-time = "2025-10-08T19:46:24.597Z" }, { url = "https://files.pythonhosted.org/packages/c2/21/d7b68e911f9c8e18e4ae43bdbc1e1e9bbd971f8866eb81608947b6f585ff/propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5", size = 45777, upload-time = "2025-10-08T19:46:25.733Z" }, { url = "https://files.pythonhosted.org/packages/d3/1d/11605e99ac8ea9435651ee71ab4cb4bf03f0949586246476a25aadfec54a/propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e", size = 47647, upload-time = "2025-10-08T19:46:27.304Z" }, { url = "https://files.pythonhosted.org/packages/58/1a/3c62c127a8466c9c843bccb503d40a273e5cc69838805f322e2826509e0d/propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566", size = 214929, upload-time = "2025-10-08T19:46:28.62Z" }, { url = "https://files.pythonhosted.org/packages/56/b9/8fa98f850960b367c4b8fe0592e7fc341daa7a9462e925228f10a60cf74f/propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165", size = 221778, upload-time = "2025-10-08T19:46:30.358Z" }, { url = "https://files.pythonhosted.org/packages/46/a6/0ab4f660eb59649d14b3d3d65c439421cf2f87fe5dd68591cbe3c1e78a89/propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc", size = 228144, upload-time = "2025-10-08T19:46:32.607Z" }, { url = "https://files.pythonhosted.org/packages/52/6a/57f43e054fb3d3a56ac9fc532bc684fc6169a26c75c353e65425b3e56eef/propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48", size = 210030, upload-time = "2025-10-08T19:46:33.969Z" }, { url = "https://files.pythonhosted.org/packages/40/e2/27e6feebb5f6b8408fa29f5efbb765cd54c153ac77314d27e457a3e993b7/propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570", size = 208252, upload-time = "2025-10-08T19:46:35.309Z" }, { url = "https://files.pythonhosted.org/packages/9e/f8/91c27b22ccda1dbc7967f921c42825564fa5336a01ecd72eb78a9f4f53c2/propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85", size = 202064, upload-time = "2025-10-08T19:46:36.993Z" }, { url = "https://files.pythonhosted.org/packages/f2/26/7f00bd6bd1adba5aafe5f4a66390f243acab58eab24ff1a08bebb2ef9d40/propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e", size = 212429, upload-time = "2025-10-08T19:46:38.398Z" }, { url = "https://files.pythonhosted.org/packages/84/89/fd108ba7815c1117ddca79c228f3f8a15fc82a73bca8b142eb5de13b2785/propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757", size = 216727, upload-time = "2025-10-08T19:46:39.732Z" }, { url = "https://files.pythonhosted.org/packages/79/37/3ec3f7e3173e73f1d600495d8b545b53802cbf35506e5732dd8578db3724/propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f", size = 205097, upload-time = "2025-10-08T19:46:41.025Z" }, { url = "https://files.pythonhosted.org/packages/61/b0/b2631c19793f869d35f47d5a3a56fb19e9160d3c119f15ac7344fc3ccae7/propcache-0.4.1-cp311-cp311-win32.whl", hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1", size = 38084, upload-time = "2025-10-08T19:46:42.693Z" }, { url = "https://files.pythonhosted.org/packages/f4/78/6cce448e2098e9f3bfc91bb877f06aa24b6ccace872e39c53b2f707c4648/propcache-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6", size = 41637, upload-time = "2025-10-08T19:46:43.778Z" }, { url = "https://files.pythonhosted.org/packages/9c/e9/754f180cccd7f51a39913782c74717c581b9cc8177ad0e949f4d51812383/propcache-0.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239", size = 38064, upload-time = "2025-10-08T19:46:44.872Z" }, { url = "https://files.pythonhosted.org/packages/a2/0f/f17b1b2b221d5ca28b4b876e8bb046ac40466513960646bda8e1853cdfa2/propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2", size = 80061, upload-time = "2025-10-08T19:46:46.075Z" }, { url = "https://files.pythonhosted.org/packages/76/47/8ccf75935f51448ba9a16a71b783eb7ef6b9ee60f5d14c7f8a8a79fbeed7/propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403", size = 46037, upload-time = "2025-10-08T19:46:47.23Z" }, { url = "https://files.pythonhosted.org/packages/0a/b6/5c9a0e42df4d00bfb4a3cbbe5cf9f54260300c88a0e9af1f47ca5ce17ac0/propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207", size = 47324, upload-time = "2025-10-08T19:46:48.384Z" }, { url = "https://files.pythonhosted.org/packages/9e/d3/6c7ee328b39a81ee877c962469f1e795f9db87f925251efeb0545e0020d0/propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72", size = 225505, upload-time = "2025-10-08T19:46:50.055Z" }, { url = "https://files.pythonhosted.org/packages/01/5d/1c53f4563490b1d06a684742cc6076ef944bc6457df6051b7d1a877c057b/propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367", size = 230242, upload-time = "2025-10-08T19:46:51.815Z" }, { url = "https://files.pythonhosted.org/packages/20/e1/ce4620633b0e2422207c3cb774a0ee61cac13abc6217763a7b9e2e3f4a12/propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4", size = 238474, upload-time = "2025-10-08T19:46:53.208Z" }, { url = "https://files.pythonhosted.org/packages/46/4b/3aae6835b8e5f44ea6a68348ad90f78134047b503765087be2f9912140ea/propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf", size = 221575, upload-time = "2025-10-08T19:46:54.511Z" }, { url = "https://files.pythonhosted.org/packages/6e/a5/8a5e8678bcc9d3a1a15b9a29165640d64762d424a16af543f00629c87338/propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3", size = 216736, upload-time = "2025-10-08T19:46:56.212Z" }, { url = "https://files.pythonhosted.org/packages/f1/63/b7b215eddeac83ca1c6b934f89d09a625aa9ee4ba158338854c87210cc36/propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778", size = 213019, upload-time = "2025-10-08T19:46:57.595Z" }, { url = "https://files.pythonhosted.org/packages/57/74/f580099a58c8af587cac7ba19ee7cb418506342fbbe2d4a4401661cca886/propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6", size = 220376, upload-time = "2025-10-08T19:46:59.067Z" }, { url = "https://files.pythonhosted.org/packages/c4/ee/542f1313aff7eaf19c2bb758c5d0560d2683dac001a1c96d0774af799843/propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9", size = 226988, upload-time = "2025-10-08T19:47:00.544Z" }, { url = "https://files.pythonhosted.org/packages/8f/18/9c6b015dd9c6930f6ce2229e1f02fb35298b847f2087ea2b436a5bfa7287/propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75", size = 215615, upload-time = "2025-10-08T19:47:01.968Z" }, { url = "https://files.pythonhosted.org/packages/80/9e/e7b85720b98c45a45e1fca6a177024934dc9bc5f4d5dd04207f216fc33ed/propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8", size = 38066, upload-time = "2025-10-08T19:47:03.503Z" }, { url = "https://files.pythonhosted.org/packages/54/09/d19cff2a5aaac632ec8fc03737b223597b1e347416934c1b3a7df079784c/propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db", size = 41655, upload-time = "2025-10-08T19:47:04.973Z" }, { url = "https://files.pythonhosted.org/packages/68/ab/6b5c191bb5de08036a8c697b265d4ca76148efb10fa162f14af14fb5f076/propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1", size = 37789, upload-time = "2025-10-08T19:47:06.077Z" }, { url = "https://files.pythonhosted.org/packages/bf/df/6d9c1b6ac12b003837dde8a10231a7344512186e87b36e855bef32241942/propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf", size = 77750, upload-time = "2025-10-08T19:47:07.648Z" }, { url = "https://files.pythonhosted.org/packages/8b/e8/677a0025e8a2acf07d3418a2e7ba529c9c33caf09d3c1f25513023c1db56/propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311", size = 44780, upload-time = "2025-10-08T19:47:08.851Z" }, { url = "https://files.pythonhosted.org/packages/89/a4/92380f7ca60f99ebae761936bc48a72a639e8a47b29050615eef757cb2a7/propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74", size = 46308, upload-time = "2025-10-08T19:47:09.982Z" }, { url = "https://files.pythonhosted.org/packages/2d/48/c5ac64dee5262044348d1d78a5f85dd1a57464a60d30daee946699963eb3/propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe", size = 208182, upload-time = "2025-10-08T19:47:11.319Z" }, { url = "https://files.pythonhosted.org/packages/c6/0c/cd762dd011a9287389a6a3eb43aa30207bde253610cca06824aeabfe9653/propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af", size = 211215, upload-time = "2025-10-08T19:47:13.146Z" }, { url = "https://files.pythonhosted.org/packages/30/3e/49861e90233ba36890ae0ca4c660e95df565b2cd15d4a68556ab5865974e/propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c", size = 218112, upload-time = "2025-10-08T19:47:14.913Z" }, { url = "https://files.pythonhosted.org/packages/f1/8b/544bc867e24e1bd48f3118cecd3b05c694e160a168478fa28770f22fd094/propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f", size = 204442, upload-time = "2025-10-08T19:47:16.277Z" }, { url = "https://files.pythonhosted.org/packages/50/a6/4282772fd016a76d3e5c0df58380a5ea64900afd836cec2c2f662d1b9bb3/propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1", size = 199398, upload-time = "2025-10-08T19:47:17.962Z" }, { url = "https://files.pythonhosted.org/packages/3e/ec/d8a7cd406ee1ddb705db2139f8a10a8a427100347bd698e7014351c7af09/propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24", size = 196920, upload-time = "2025-10-08T19:47:19.355Z" }, { url = "https://files.pythonhosted.org/packages/f6/6c/f38ab64af3764f431e359f8baf9e0a21013e24329e8b85d2da32e8ed07ca/propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa", size = 203748, upload-time = "2025-10-08T19:47:21.338Z" }, { url = "https://files.pythonhosted.org/packages/d6/e3/fa846bd70f6534d647886621388f0a265254d30e3ce47e5c8e6e27dbf153/propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61", size = 205877, upload-time = "2025-10-08T19:47:23.059Z" }, { url = "https://files.pythonhosted.org/packages/e2/39/8163fc6f3133fea7b5f2827e8eba2029a0277ab2c5beee6c1db7b10fc23d/propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66", size = 199437, upload-time = "2025-10-08T19:47:24.445Z" }, { url = "https://files.pythonhosted.org/packages/93/89/caa9089970ca49c7c01662bd0eeedfe85494e863e8043565aeb6472ce8fe/propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81", size = 37586, upload-time = "2025-10-08T19:47:25.736Z" }, { url = "https://files.pythonhosted.org/packages/f5/ab/f76ec3c3627c883215b5c8080debb4394ef5a7a29be811f786415fc1e6fd/propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e", size = 40790, upload-time = "2025-10-08T19:47:26.847Z" }, { url = "https://files.pythonhosted.org/packages/59/1b/e71ae98235f8e2ba5004d8cb19765a74877abf189bc53fc0c80d799e56c3/propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1", size = 37158, upload-time = "2025-10-08T19:47:27.961Z" }, { url = "https://files.pythonhosted.org/packages/83/ce/a31bbdfc24ee0dcbba458c8175ed26089cf109a55bbe7b7640ed2470cfe9/propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b", size = 81451, upload-time = "2025-10-08T19:47:29.445Z" }, { url = "https://files.pythonhosted.org/packages/25/9c/442a45a470a68456e710d96cacd3573ef26a1d0a60067e6a7d5e655621ed/propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566", size = 46374, upload-time = "2025-10-08T19:47:30.579Z" }, { url = "https://files.pythonhosted.org/packages/f4/bf/b1d5e21dbc3b2e889ea4327044fb16312a736d97640fb8b6aa3f9c7b3b65/propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835", size = 48396, upload-time = "2025-10-08T19:47:31.79Z" }, { url = "https://files.pythonhosted.org/packages/f4/04/5b4c54a103d480e978d3c8a76073502b18db0c4bc17ab91b3cb5092ad949/propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e", size = 275950, upload-time = "2025-10-08T19:47:33.481Z" }, { url = "https://files.pythonhosted.org/packages/b4/c1/86f846827fb969c4b78b0af79bba1d1ea2156492e1b83dea8b8a6ae27395/propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859", size = 273856, upload-time = "2025-10-08T19:47:34.906Z" }, { url = "https://files.pythonhosted.org/packages/36/1d/fc272a63c8d3bbad6878c336c7a7dea15e8f2d23a544bda43205dfa83ada/propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b", size = 280420, upload-time = "2025-10-08T19:47:36.338Z" }, { url = "https://files.pythonhosted.org/packages/07/0c/01f2219d39f7e53d52e5173bcb09c976609ba30209912a0680adfb8c593a/propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0", size = 263254, upload-time = "2025-10-08T19:47:37.692Z" }, { url = "https://files.pythonhosted.org/packages/2d/18/cd28081658ce597898f0c4d174d4d0f3c5b6d4dc27ffafeef835c95eb359/propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af", size = 261205, upload-time = "2025-10-08T19:47:39.659Z" }, { url = "https://files.pythonhosted.org/packages/7a/71/1f9e22eb8b8316701c2a19fa1f388c8a3185082607da8e406a803c9b954e/propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393", size = 247873, upload-time = "2025-10-08T19:47:41.084Z" }, { url = "https://files.pythonhosted.org/packages/4a/65/3d4b61f36af2b4eddba9def857959f1016a51066b4f1ce348e0cf7881f58/propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874", size = 262739, upload-time = "2025-10-08T19:47:42.51Z" }, { url = "https://files.pythonhosted.org/packages/2a/42/26746ab087faa77c1c68079b228810436ccd9a5ce9ac85e2b7307195fd06/propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7", size = 263514, upload-time = "2025-10-08T19:47:43.927Z" }, { url = "https://files.pythonhosted.org/packages/94/13/630690fe201f5502d2403dd3cfd451ed8858fe3c738ee88d095ad2ff407b/propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1", size = 257781, upload-time = "2025-10-08T19:47:45.448Z" }, { url = "https://files.pythonhosted.org/packages/92/f7/1d4ec5841505f423469efbfc381d64b7b467438cd5a4bbcbb063f3b73d27/propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717", size = 41396, upload-time = "2025-10-08T19:47:47.202Z" }, { url = "https://files.pythonhosted.org/packages/48/f0/615c30622316496d2cbbc29f5985f7777d3ada70f23370608c1d3e081c1f/propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37", size = 44897, upload-time = "2025-10-08T19:47:48.336Z" }, { url = "https://files.pythonhosted.org/packages/fd/ca/6002e46eccbe0e33dcd4069ef32f7f1c9e243736e07adca37ae8c4830ec3/propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a", size = 39789, upload-time = "2025-10-08T19:47:49.876Z" }, { url = "https://files.pythonhosted.org/packages/8e/5c/bca52d654a896f831b8256683457ceddd490ec18d9ec50e97dfd8fc726a8/propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12", size = 78152, upload-time = "2025-10-08T19:47:51.051Z" }, { url = "https://files.pythonhosted.org/packages/65/9b/03b04e7d82a5f54fb16113d839f5ea1ede58a61e90edf515f6577c66fa8f/propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c", size = 44869, upload-time = "2025-10-08T19:47:52.594Z" }, { url = "https://files.pythonhosted.org/packages/b2/fa/89a8ef0468d5833a23fff277b143d0573897cf75bd56670a6d28126c7d68/propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded", size = 46596, upload-time = "2025-10-08T19:47:54.073Z" }, { url = "https://files.pythonhosted.org/packages/86/bd/47816020d337f4a746edc42fe8d53669965138f39ee117414c7d7a340cfe/propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641", size = 206981, upload-time = "2025-10-08T19:47:55.715Z" }, { url = "https://files.pythonhosted.org/packages/df/f6/c5fa1357cc9748510ee55f37173eb31bfde6d94e98ccd9e6f033f2fc06e1/propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4", size = 211490, upload-time = "2025-10-08T19:47:57.499Z" }, { url = "https://files.pythonhosted.org/packages/80/1e/e5889652a7c4a3846683401a48f0f2e5083ce0ec1a8a5221d8058fbd1adf/propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44", size = 215371, upload-time = "2025-10-08T19:47:59.317Z" }, { url = "https://files.pythonhosted.org/packages/b2/f2/889ad4b2408f72fe1a4f6a19491177b30ea7bf1a0fd5f17050ca08cfc882/propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d", size = 201424, upload-time = "2025-10-08T19:48:00.67Z" }, { url = "https://files.pythonhosted.org/packages/27/73/033d63069b57b0812c8bd19f311faebeceb6ba31b8f32b73432d12a0b826/propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b", size = 197566, upload-time = "2025-10-08T19:48:02.604Z" }, { url = "https://files.pythonhosted.org/packages/dc/89/ce24f3dc182630b4e07aa6d15f0ff4b14ed4b9955fae95a0b54c58d66c05/propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e", size = 193130, upload-time = "2025-10-08T19:48:04.499Z" }, { url = "https://files.pythonhosted.org/packages/a9/24/ef0d5fd1a811fb5c609278d0209c9f10c35f20581fcc16f818da959fc5b4/propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f", size = 202625, upload-time = "2025-10-08T19:48:06.213Z" }, { url = "https://files.pythonhosted.org/packages/f5/02/98ec20ff5546f68d673df2f7a69e8c0d076b5abd05ca882dc7ee3a83653d/propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49", size = 204209, upload-time = "2025-10-08T19:48:08.432Z" }, { url = "https://files.pythonhosted.org/packages/a0/87/492694f76759b15f0467a2a93ab68d32859672b646aa8a04ce4864e7932d/propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144", size = 197797, upload-time = "2025-10-08T19:48:09.968Z" }, { url = "https://files.pythonhosted.org/packages/ee/36/66367de3575db1d2d3f3d177432bd14ee577a39d3f5d1b3d5df8afe3b6e2/propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f", size = 38140, upload-time = "2025-10-08T19:48:11.232Z" }, { url = "https://files.pythonhosted.org/packages/0c/2a/a758b47de253636e1b8aef181c0b4f4f204bf0dd964914fb2af90a95b49b/propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153", size = 41257, upload-time = "2025-10-08T19:48:12.707Z" }, { url = "https://files.pythonhosted.org/packages/34/5e/63bd5896c3fec12edcbd6f12508d4890d23c265df28c74b175e1ef9f4f3b/propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992", size = 38097, upload-time = "2025-10-08T19:48:13.923Z" }, { url = "https://files.pythonhosted.org/packages/99/85/9ff785d787ccf9bbb3f3106f79884a130951436f58392000231b4c737c80/propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f", size = 81455, upload-time = "2025-10-08T19:48:15.16Z" }, { url = "https://files.pythonhosted.org/packages/90/85/2431c10c8e7ddb1445c1f7c4b54d886e8ad20e3c6307e7218f05922cad67/propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393", size = 46372, upload-time = "2025-10-08T19:48:16.424Z" }, { url = "https://files.pythonhosted.org/packages/01/20/b0972d902472da9bcb683fa595099911f4d2e86e5683bcc45de60dd05dc3/propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0", size = 48411, upload-time = "2025-10-08T19:48:17.577Z" }, { url = "https://files.pythonhosted.org/packages/e2/e3/7dc89f4f21e8f99bad3d5ddb3a3389afcf9da4ac69e3deb2dcdc96e74169/propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a", size = 275712, upload-time = "2025-10-08T19:48:18.901Z" }, { url = "https://files.pythonhosted.org/packages/20/67/89800c8352489b21a8047c773067644e3897f02ecbbd610f4d46b7f08612/propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be", size = 273557, upload-time = "2025-10-08T19:48:20.762Z" }, { url = "https://files.pythonhosted.org/packages/e2/a1/b52b055c766a54ce6d9c16d9aca0cad8059acd9637cdf8aa0222f4a026ef/propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc", size = 280015, upload-time = "2025-10-08T19:48:22.592Z" }, { url = "https://files.pythonhosted.org/packages/48/c8/33cee30bd890672c63743049f3c9e4be087e6780906bfc3ec58528be59c1/propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a", size = 262880, upload-time = "2025-10-08T19:48:23.947Z" }, { url = "https://files.pythonhosted.org/packages/0c/b1/8f08a143b204b418285c88b83d00edbd61afbc2c6415ffafc8905da7038b/propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89", size = 260938, upload-time = "2025-10-08T19:48:25.656Z" }, { url = "https://files.pythonhosted.org/packages/cf/12/96e4664c82ca2f31e1c8dff86afb867348979eb78d3cb8546a680287a1e9/propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726", size = 247641, upload-time = "2025-10-08T19:48:27.207Z" }, { url = "https://files.pythonhosted.org/packages/18/ed/e7a9cfca28133386ba52278136d42209d3125db08d0a6395f0cba0c0285c/propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367", size = 262510, upload-time = "2025-10-08T19:48:28.65Z" }, { url = "https://files.pythonhosted.org/packages/f5/76/16d8bf65e8845dd62b4e2b57444ab81f07f40caa5652b8969b87ddcf2ef6/propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36", size = 263161, upload-time = "2025-10-08T19:48:30.133Z" }, { url = "https://files.pythonhosted.org/packages/e7/70/c99e9edb5d91d5ad8a49fa3c1e8285ba64f1476782fed10ab251ff413ba1/propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455", size = 257393, upload-time = "2025-10-08T19:48:31.567Z" }, { url = "https://files.pythonhosted.org/packages/08/02/87b25304249a35c0915d236575bc3574a323f60b47939a2262b77632a3ee/propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85", size = 42546, upload-time = "2025-10-08T19:48:32.872Z" }, { url = "https://files.pythonhosted.org/packages/cb/ef/3c6ecf8b317aa982f309835e8f96987466123c6e596646d4e6a1dfcd080f/propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1", size = 46259, upload-time = "2025-10-08T19:48:34.226Z" }, { url = "https://files.pythonhosted.org/packages/c4/2d/346e946d4951f37eca1e4f55be0f0174c52cd70720f84029b02f296f4a38/propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9", size = 40428, upload-time = "2025-10-08T19:48:35.441Z" }, { url = "https://files.pythonhosted.org/packages/9b/01/0ebaec9003f5d619a7475165961f8e3083cf8644d704b60395df3601632d/propcache-0.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3d233076ccf9e450c8b3bc6720af226b898ef5d051a2d145f7d765e6e9f9bcff", size = 80277, upload-time = "2025-10-08T19:48:36.647Z" }, { url = "https://files.pythonhosted.org/packages/34/58/04af97ac586b4ef6b9026c3fd36ee7798b737a832f5d3440a4280dcebd3a/propcache-0.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:357f5bb5c377a82e105e44bd3d52ba22b616f7b9773714bff93573988ef0a5fb", size = 45865, upload-time = "2025-10-08T19:48:37.859Z" }, { url = "https://files.pythonhosted.org/packages/7c/19/b65d98ae21384518b291d9939e24a8aeac4fdb5101b732576f8f7540e834/propcache-0.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cbc3b6dfc728105b2a57c06791eb07a94229202ea75c59db644d7d496b698cac", size = 47636, upload-time = "2025-10-08T19:48:39.038Z" }, { url = "https://files.pythonhosted.org/packages/b3/0f/317048c6d91c356c7154dca5af019e6effeb7ee15fa6a6db327cc19e12b4/propcache-0.4.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:182b51b421f0501952d938dc0b0eb45246a5b5153c50d42b495ad5fb7517c888", size = 201126, upload-time = "2025-10-08T19:48:40.774Z" }, { url = "https://files.pythonhosted.org/packages/71/69/0b2a7a5a6ee83292b4b997dbd80549d8ce7d40b6397c1646c0d9495f5a85/propcache-0.4.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4b536b39c5199b96fc6245eb5fb796c497381d3942f169e44e8e392b29c9ebcc", size = 209837, upload-time = "2025-10-08T19:48:42.167Z" }, { url = "https://files.pythonhosted.org/packages/a5/92/c699ac495a6698df6e497fc2de27af4b6ace10d8e76528357ce153722e45/propcache-0.4.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:db65d2af507bbfbdcedb254a11149f894169d90488dd3e7190f7cdcb2d6cd57a", size = 215578, upload-time = "2025-10-08T19:48:43.56Z" }, { url = "https://files.pythonhosted.org/packages/b3/ee/14de81c5eb02c0ee4f500b4e39c4e1bd0677c06e72379e6ab18923c773fc/propcache-0.4.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd2dbc472da1f772a4dae4fa24be938a6c544671a912e30529984dd80400cd88", size = 197187, upload-time = "2025-10-08T19:48:45.309Z" }, { url = "https://files.pythonhosted.org/packages/1d/94/48dce9aaa6d8dd5a0859bad75158ec522546d4ac23f8e2f05fac469477dd/propcache-0.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:daede9cd44e0f8bdd9e6cc9a607fc81feb80fae7a5fc6cecaff0e0bb32e42d00", size = 193478, upload-time = "2025-10-08T19:48:47.743Z" }, { url = "https://files.pythonhosted.org/packages/60/b5/0516b563e801e1ace212afde869a0596a0d7115eec0b12d296d75633fb29/propcache-0.4.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:71b749281b816793678ae7f3d0d84bd36e694953822eaad408d682efc5ca18e0", size = 190650, upload-time = "2025-10-08T19:48:49.373Z" }, { url = "https://files.pythonhosted.org/packages/24/89/e0f7d4a5978cd56f8cd67735f74052f257dc471ec901694e430f0d1572fe/propcache-0.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:0002004213ee1f36cfb3f9a42b5066100c44276b9b72b4e1504cddd3d692e86e", size = 200251, upload-time = "2025-10-08T19:48:51.4Z" }, { url = "https://files.pythonhosted.org/packages/06/7d/a1fac863d473876ed4406c914f2e14aa82d2f10dd207c9e16fc383cc5a24/propcache-0.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:fe49d0a85038f36ba9e3ffafa1103e61170b28e95b16622e11be0a0ea07c6781", size = 200919, upload-time = "2025-10-08T19:48:53.227Z" }, { url = "https://files.pythonhosted.org/packages/c3/4e/f86a256ff24944cf5743e4e6c6994e3526f6acfcfb55e21694c2424f758c/propcache-0.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:99d43339c83aaf4d32bda60928231848eee470c6bda8d02599cc4cebe872d183", size = 193211, upload-time = "2025-10-08T19:48:55.027Z" }, { url = "https://files.pythonhosted.org/packages/6e/3f/3fbad5f4356b068f1b047d300a6ff2c66614d7030f078cd50be3fec04228/propcache-0.4.1-cp39-cp39-win32.whl", hash = "sha256:a129e76735bc792794d5177069691c3217898b9f5cee2b2661471e52ffe13f19", size = 38314, upload-time = "2025-10-08T19:48:56.792Z" }, { url = "https://files.pythonhosted.org/packages/a4/45/d78d136c3a3d215677abb886785aae744da2c3005bcb99e58640c56529b1/propcache-0.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:948dab269721ae9a87fd16c514a0a2c2a1bdb23a9a61b969b0f9d9ee2968546f", size = 41912, upload-time = "2025-10-08T19:48:57.995Z" }, { url = "https://files.pythonhosted.org/packages/fc/2a/b0632941f25139f4e58450b307242951f7c2717a5704977c6d5323a800af/propcache-0.4.1-cp39-cp39-win_arm64.whl", hash = "sha256:5fd37c406dd6dc85aa743e214cef35dc54bbdd1419baac4f6ae5e5b1a2976938", size = 38450, upload-time = "2025-10-08T19:48:59.349Z" }, { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, ] [[package]] name = "pyasn1" version = "0.6.3" 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" } 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" }, ] [[package]] name = "pyasn1-modules" version = "0.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyasn1" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" }, ] [[package]] name = "pycparser" version = "2.23" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.10'", ] sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" }, ] [[package]] name = "pycparser" version = "3.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.14' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and python_full_version < '3.14' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and python_full_version < '3.14' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra != 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", ] sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, ] [[package]] name = "pydantic" version = "1.10.26" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.10'", "python_full_version < '3.10'", ] dependencies = [ { name = "typing-extensions", marker = "extra == 'group-9-anthropic-pydantic-v1'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/7b/da/fd89f987a376c807cd81ea0eff4589aade783bbb702637b4734ef2c743a2/pydantic-1.10.26.tar.gz", hash = "sha256:8c6aa39b494c5af092e690127c283d84f363ac36017106a9e66cb33a22ac412e", size = 357906, upload-time = "2025-12-18T15:47:46.557Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/71/08/2587a6d4314e7539eec84acd062cb7b037638edb57a0335d20e4c5b8878c/pydantic-1.10.26-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f7ae36fa0ecef8d39884120f212e16c06bb096a38f523421278e2f39c1784546", size = 2444588, upload-time = "2025-12-18T15:46:28.882Z" }, { url = "https://files.pythonhosted.org/packages/47/e6/10df5f08c105bcbb4adbee7d1108ff4b347702b110fed058f6a03f1c6b73/pydantic-1.10.26-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d95a76cf503f0f72ed7812a91de948440b2bf564269975738a4751e4fadeb572", size = 2255972, upload-time = "2025-12-18T15:46:31.72Z" }, { url = "https://files.pythonhosted.org/packages/ba/7d/fdb961e7adc2c31f394feba6f560ef2c74c446f0285e2c2eb87d2b7206c7/pydantic-1.10.26-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a943ce8e00ad708ed06a1d9df5b4fd28f5635a003b82a4908ece6f24c0b18464", size = 2857175, upload-time = "2025-12-18T15:46:34Z" }, { url = "https://files.pythonhosted.org/packages/8f/6c/f21e27dda475d4c562bd01b5874284dd3180f336c1e669413b743ca8b278/pydantic-1.10.26-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:465ad8edb29b15c10b779b16431fe8e77c380098badf6db367b7a1d3e572cf53", size = 2947001, upload-time = "2025-12-18T15:46:35.922Z" }, { url = "https://files.pythonhosted.org/packages/6d/f6/27ea206232cbb6ec24dc4e4e8888a9a734f96a1eaf13504be4b30ef26aa7/pydantic-1.10.26-cp310-cp310-win_amd64.whl", hash = "sha256:80e6be6272839c8a7641d26ad569ab77772809dd78f91d0068dc0fc97f071945", size = 2066217, upload-time = "2025-12-18T15:46:37.614Z" }, { url = "https://files.pythonhosted.org/packages/1d/c1/d521e64c8130e1ad9d22c270bed3fabcc0940c9539b076b639c88fd32a8d/pydantic-1.10.26-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:116233e53889bcc536f617e38c1b8337d7fa9c280f0fd7a4045947515a785637", size = 2428347, upload-time = "2025-12-18T15:46:39.41Z" }, { url = "https://files.pythonhosted.org/packages/2c/08/f4b804a00c16e3ea994cb640a7c25c579b4f1fa674cde6a19fa0dfb0ae4f/pydantic-1.10.26-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c3cfdd361addb6eb64ccd26ac356ad6514cee06a61ab26b27e16b5ed53108f77", size = 2212605, upload-time = "2025-12-18T15:46:41.006Z" }, { url = "https://files.pythonhosted.org/packages/5d/78/0df4b9efef29bbc5e39f247fcba99060d15946b4463d82a5589cf7923d71/pydantic-1.10.26-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0e4451951a9a93bf9a90576f3e25240b47ee49ab5236adccb8eff6ac943adf0f", size = 2753560, upload-time = "2025-12-18T15:46:43.215Z" }, { url = "https://files.pythonhosted.org/packages/68/66/6ab6c1d3a116d05d2508fce64f96e35242938fac07544d611e11d0d363a0/pydantic-1.10.26-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9858ed44c6bea5f29ffe95308db9e62060791c877766c67dd5f55d072c8612b5", size = 2859235, upload-time = "2025-12-18T15:46:45.112Z" }, { url = "https://files.pythonhosted.org/packages/61/4e/f1676bb0fcdf6ed2ce4670d7d1fc1d6c3a06d84497644acfbe02649503f1/pydantic-1.10.26-cp311-cp311-win_amd64.whl", hash = "sha256:ac1089f723e2106ebde434377d31239e00870a7563245072968e5af5cc4d33df", size = 2066646, upload-time = "2025-12-18T15:46:46.816Z" }, { url = "https://files.pythonhosted.org/packages/02/6c/cd97a5a776c4515e6ee2ae81c2f2c5be51376dda6c31f965d7746ce0019f/pydantic-1.10.26-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:468d5b9cacfcaadc76ed0a4645354ab6f263ec01a63fb6d05630ea1df6ae453f", size = 2433795, upload-time = "2025-12-18T15:46:49.321Z" }, { url = "https://files.pythonhosted.org/packages/47/12/de20affa30dcef728fcf9cc98e13ff4438c7a630de8d2f90eb38eba0891c/pydantic-1.10.26-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2c1b0b914be31671000ca25cf7ea17fcaaa68cfeadf6924529c5c5aa24b7ab1f", size = 2227387, upload-time = "2025-12-18T15:46:50.877Z" }, { url = "https://files.pythonhosted.org/packages/7b/1d/9d65dcc5b8c17ba590f1f9f486e9306346831902318b7ee93f63516f4003/pydantic-1.10.26-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15b13b9f8ba8867095769e1156e0d7fbafa1f65b898dd40fd1c02e34430973cb", size = 2629594, upload-time = "2025-12-18T15:46:53.42Z" }, { url = "https://files.pythonhosted.org/packages/3f/76/acb41409356789e23e1a7ef58f93821410c96409183ce314ddb58d97f23e/pydantic-1.10.26-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad7025ca324ae263d4313998e25078dcaec5f9ed0392c06dedb57e053cc8086b", size = 2745305, upload-time = "2025-12-18T15:46:55.987Z" }, { url = "https://files.pythonhosted.org/packages/22/72/a98c0c5e527a66057d969fedd61675223c7975ade61acebbca9f1abd6dc0/pydantic-1.10.26-cp312-cp312-win_amd64.whl", hash = "sha256:4482b299874dabb88a6c3759e3d85c6557c407c3b586891f7d808d8a38b66b9c", size = 1937647, upload-time = "2025-12-18T15:46:57.905Z" }, { url = "https://files.pythonhosted.org/packages/28/b9/17a5a5a421c23ac27486b977724a42c9d5f8b7f0f4aab054251066223900/pydantic-1.10.26-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1ae7913bb40a96c87e3d3f6fe4e918ef53bf181583de4e71824360a9b11aef1c", size = 2494599, upload-time = "2025-12-18T15:47:00.209Z" }, { url = "https://files.pythonhosted.org/packages/e6/8e/6e3bd4241076cf227b443d7577245dd5d181ecf40b3182fcb908bc8c197d/pydantic-1.10.26-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8154c13f58d4de5d3a856bb6c909c7370f41fb876a5952a503af6b975265f4ba", size = 2254391, upload-time = "2025-12-18T15:47:02.268Z" }, { url = "https://files.pythonhosted.org/packages/a8/30/a1c4092eda2145ecbead6c92db489b223e101e1ba0da82576d0cf73dd422/pydantic-1.10.26-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f8af0507bf6118b054a9765fb2e402f18a8b70c964f420d95b525eb711122d62", size = 2609445, upload-time = "2025-12-18T15:47:04.909Z" }, { url = "https://files.pythonhosted.org/packages/3a/2a/0491f1729ee4b7b6bc859ec22f69752f0c09bee1b66ac6f5f701136f34c3/pydantic-1.10.26-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dcb5a7318fb43189fde6af6f21ac7149c4bcbcfffc54bc87b5becddc46084847", size = 2732124, upload-time = "2025-12-18T15:47:07.464Z" }, { url = "https://files.pythonhosted.org/packages/2a/56/b59f3b2f84e1df2b04ae768a1bb04d9f0288ff71b67cdcbb07683757b2c0/pydantic-1.10.26-cp313-cp313-win_amd64.whl", hash = "sha256:71cde228bc0600cf8619f0ee62db050d1880dcc477eba0e90b23011b4ee0f314", size = 1939888, upload-time = "2025-12-18T15:47:09.618Z" }, { url = "https://files.pythonhosted.org/packages/d2/8b/0c3dc02d4b97790b0f199bf933f677c14e7be4a8d21307c5f2daae06aa41/pydantic-1.10.26-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:6b40730cc81d53d515dc0b8bb5c9b43fadb9bed46de4a3c03bd95e8571616dba", size = 2502689, upload-time = "2025-12-18T15:47:12.308Z" }, { url = "https://files.pythonhosted.org/packages/d4/9d/d31aeea45542b2ae4b09ecba92b88aaba696b801c31919811aa979a1242d/pydantic-1.10.26-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c3bbb9c0eecdf599e4db9b372fa9cc55be12e80a0d9c6d307950a39050cb0e37", size = 2269494, upload-time = "2025-12-18T15:47:14.53Z" }, { url = "https://files.pythonhosted.org/packages/78/c1/3a4d069593283ca4dd0006039ba33644e21e432cddc09da706ac50441610/pydantic-1.10.26-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc2e3fe7bc4993626ef6b6fa855defafa1d6f8996aa1caef2deb83c5ac4d043a", size = 2620047, upload-time = "2025-12-18T15:47:17.089Z" }, { url = "https://files.pythonhosted.org/packages/e0/0e/340c3d29197d99c15ab04093d43bb9c9d0fd17c2a34b80cb9d36ed732b09/pydantic-1.10.26-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:36d9e46b588aaeb1dcd2409fa4c467fe0b331f3cc9f227b03a7a00643704e962", size = 2747625, upload-time = "2025-12-18T15:47:19.21Z" }, { url = "https://files.pythonhosted.org/packages/1e/58/f12ab3727339b172c830b32151919456b67787cdfe8808b2568b322fb15c/pydantic-1.10.26-cp314-cp314-win_amd64.whl", hash = "sha256:81ce3c8616d12a7be31b4aadfd3434f78f6b44b75adbfaec2fe1ad4f7f999b8c", size = 1976436, upload-time = "2025-12-18T15:47:21.384Z" }, { url = "https://files.pythonhosted.org/packages/e1/8a/3a5a6267d5f03617b5c0f1985aa9fdfbafd33a50ef6dadd866a15ed4d123/pydantic-1.10.26-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:502b9d30d18a2dfaf81b7302f6ba0e5853474b1c96212449eb4db912cb604b7d", size = 2457039, upload-time = "2025-12-18T15:47:34.584Z" }, { url = "https://files.pythonhosted.org/packages/f3/fa/343ac0db26918a033ac6256c036d72c3b6eb1196b7de622e2e8a94b19079/pydantic-1.10.26-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0d8f6087bf697dec3bf7ffcd7fe8362674f16519f3151789f33cbe8f1d19fc15", size = 2266441, upload-time = "2025-12-18T15:47:36.807Z" }, { url = "https://files.pythonhosted.org/packages/fc/36/1ab48136578608dba2f2a62e452f3db2083b474d4e49be5749c6ae0c123c/pydantic-1.10.26-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dd40a99c358419910c85e6f5d22f9c56684c25b5e7abc40879b3b4a52f34ae90", size = 2869383, upload-time = "2025-12-18T15:47:38.883Z" }, { url = "https://files.pythonhosted.org/packages/a2/25/41dbf1bffc31eb242cece8080561a4133eaeb513372dec36a84477a3fb71/pydantic-1.10.26-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ce3293b86ca9f4125df02ff0a70be91bc7946522467cbd98e7f1493f340616ba", size = 2963582, upload-time = "2025-12-18T15:47:40.854Z" }, { url = "https://files.pythonhosted.org/packages/61/2f/f072ae160a300c85eb9f059915101fd33dacf12d8df08c2b804acb3b95d1/pydantic-1.10.26-cp39-cp39-win_amd64.whl", hash = "sha256:1a4e3062b71ab1d5df339ba12c48f9ed5817c5de6cb92a961dd5c64bb32e7b96", size = 2075530, upload-time = "2025-12-18T15:47:43.181Z" }, { url = "https://files.pythonhosted.org/packages/1f/98/556e82f00b98486def0b8af85da95e69d2be7e367cf2431408e108bc3095/pydantic-1.10.26-py3-none-any.whl", hash = "sha256:c43ad70dc3ce7787543d563792426a16fd7895e14be4b194b5665e36459dd917", size = 166975, upload-time = "2025-12-18T15:47:44.927Z" }, ] [[package]] name = "pydantic" version = "2.12.5" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.14' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and python_full_version < '3.14' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and python_full_version < '3.14' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version < '3.10' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version < '3.10' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", "python_full_version < '3.10' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", "python_full_version < '3.10' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", ] dependencies = [ { name = "annotated-types", marker = "extra == 'extra-9-anthropic-mcp' or extra != 'group-9-anthropic-pydantic-v1' or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "pydantic-core", marker = "extra == 'extra-9-anthropic-mcp' or extra != 'group-9-anthropic-pydantic-v1' or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "typing-extensions", marker = "extra == 'extra-9-anthropic-mcp' or extra != 'group-9-anthropic-pydantic-v1' or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "typing-inspection", marker = "extra == 'extra-9-anthropic-mcp' or extra != 'group-9-anthropic-pydantic-v1' or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, ] [[package]] name = "pydantic-core" version = "2.41.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions", marker = "extra == 'extra-9-anthropic-mcp' or extra != 'group-9-anthropic-pydantic-v1' or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/c6/90/32c9941e728d564b411d574d8ee0cf09b12ec978cb22b294995bae5549a5/pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146", size = 2107298, upload-time = "2025-11-04T13:39:04.116Z" }, { url = "https://files.pythonhosted.org/packages/fb/a8/61c96a77fe28993d9a6fb0f4127e05430a267b235a124545d79fea46dd65/pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2", size = 1901475, upload-time = "2025-11-04T13:39:06.055Z" }, { url = "https://files.pythonhosted.org/packages/5d/b6/338abf60225acc18cdc08b4faef592d0310923d19a87fba1faf05af5346e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97", size = 1918815, upload-time = "2025-11-04T13:39:10.41Z" }, { url = "https://files.pythonhosted.org/packages/d1/1c/2ed0433e682983d8e8cba9c8d8ef274d4791ec6a6f24c58935b90e780e0a/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9", size = 2065567, upload-time = "2025-11-04T13:39:12.244Z" }, { url = "https://files.pythonhosted.org/packages/b3/24/cf84974ee7d6eae06b9e63289b7b8f6549d416b5c199ca2d7ce13bbcf619/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52", size = 2230442, upload-time = "2025-11-04T13:39:13.962Z" }, { url = "https://files.pythonhosted.org/packages/fd/21/4e287865504b3edc0136c89c9c09431be326168b1eb7841911cbc877a995/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941", size = 2350956, upload-time = "2025-11-04T13:39:15.889Z" }, { url = "https://files.pythonhosted.org/packages/a8/76/7727ef2ffa4b62fcab916686a68a0426b9b790139720e1934e8ba797e238/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a", size = 2068253, upload-time = "2025-11-04T13:39:17.403Z" }, { url = "https://files.pythonhosted.org/packages/d5/8c/a4abfc79604bcb4c748e18975c44f94f756f08fb04218d5cb87eb0d3a63e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c", size = 2177050, upload-time = "2025-11-04T13:39:19.351Z" }, { url = "https://files.pythonhosted.org/packages/67/b1/de2e9a9a79b480f9cb0b6e8b6ba4c50b18d4e89852426364c66aa82bb7b3/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2", size = 2147178, upload-time = "2025-11-04T13:39:21Z" }, { url = "https://files.pythonhosted.org/packages/16/c1/dfb33f837a47b20417500efaa0378adc6635b3c79e8369ff7a03c494b4ac/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556", size = 2341833, upload-time = "2025-11-04T13:39:22.606Z" }, { url = "https://files.pythonhosted.org/packages/47/36/00f398642a0f4b815a9a558c4f1dca1b4020a7d49562807d7bc9ff279a6c/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49", size = 2321156, upload-time = "2025-11-04T13:39:25.843Z" }, { url = "https://files.pythonhosted.org/packages/7e/70/cad3acd89fde2010807354d978725ae111ddf6d0ea46d1ea1775b5c1bd0c/pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba", size = 1989378, upload-time = "2025-11-04T13:39:27.92Z" }, { url = "https://files.pythonhosted.org/packages/76/92/d338652464c6c367e5608e4488201702cd1cbb0f33f7b6a85a60fe5f3720/pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9", size = 2013622, upload-time = "2025-11-04T13:39:29.848Z" }, { url = "https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873, upload-time = "2025-11-04T13:39:31.373Z" }, { url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826, upload-time = "2025-11-04T13:39:32.897Z" }, { url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869, upload-time = "2025-11-04T13:39:34.469Z" }, { url = "https://files.pythonhosted.org/packages/6e/6a/30d94a9674a7fe4f4744052ed6c5e083424510be1e93da5bc47569d11810/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", size = 2063890, upload-time = "2025-11-04T13:39:36.053Z" }, { url = "https://files.pythonhosted.org/packages/50/be/76e5d46203fcb2750e542f32e6c371ffa9b8ad17364cf94bb0818dbfb50c/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", size = 2229740, upload-time = "2025-11-04T13:39:37.753Z" }, { url = "https://files.pythonhosted.org/packages/d3/ee/fed784df0144793489f87db310a6bbf8118d7b630ed07aa180d6067e653a/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", size = 2350021, upload-time = "2025-11-04T13:39:40.94Z" }, { url = "https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", size = 2066378, upload-time = "2025-11-04T13:39:42.523Z" }, { url = "https://files.pythonhosted.org/packages/b0/3b/698cf8ae1d536a010e05121b4958b1257f0b5522085e335360e53a6b1c8b/pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", size = 2175761, upload-time = "2025-11-04T13:39:44.553Z" }, { url = "https://files.pythonhosted.org/packages/b8/ba/15d537423939553116dea94ce02f9c31be0fa9d0b806d427e0308ec17145/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", size = 2146303, upload-time = "2025-11-04T13:39:46.238Z" }, { url = "https://files.pythonhosted.org/packages/58/7f/0de669bf37d206723795f9c90c82966726a2ab06c336deba4735b55af431/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", size = 2340355, upload-time = "2025-11-04T13:39:48.002Z" }, { url = "https://files.pythonhosted.org/packages/e5/de/e7482c435b83d7e3c3ee5ee4451f6e8973cff0eb6007d2872ce6383f6398/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", size = 2319875, upload-time = "2025-11-04T13:39:49.705Z" }, { url = "https://files.pythonhosted.org/packages/fe/e6/8c9e81bb6dd7560e33b9053351c29f30c8194b72f2d6932888581f503482/pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", size = 1987549, upload-time = "2025-11-04T13:39:51.842Z" }, { url = "https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", size = 2011305, upload-time = "2025-11-04T13:39:53.485Z" }, { url = "https://files.pythonhosted.org/packages/56/d8/0e271434e8efd03186c5386671328154ee349ff0354d83c74f5caaf096ed/pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", size = 1972902, upload-time = "2025-11-04T13:39:56.488Z" }, { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, { url = "https://files.pythonhosted.org/packages/54/db/160dffb57ed9a3705c4cbcbff0ac03bdae45f1ca7d58ab74645550df3fbd/pydantic_core-2.41.5-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:8bfeaf8735be79f225f3fefab7f941c712aaca36f1128c9d7e2352ee1aa87bdf", size = 2107999, upload-time = "2025-11-04T13:42:03.885Z" }, { url = "https://files.pythonhosted.org/packages/a3/7d/88e7de946f60d9263cc84819f32513520b85c0f8322f9b8f6e4afc938383/pydantic_core-2.41.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:346285d28e4c8017da95144c7f3acd42740d637ff41946af5ce6e5e420502dd5", size = 1929745, upload-time = "2025-11-04T13:42:06.075Z" }, { url = "https://files.pythonhosted.org/packages/d5/c2/aef51e5b283780e85e99ff19db0f05842d2d4a8a8cd15e63b0280029b08f/pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a75dafbf87d6276ddc5b2bf6fae5254e3d0876b626eb24969a574fff9149ee5d", size = 1920220, upload-time = "2025-11-04T13:42:08.457Z" }, { url = "https://files.pythonhosted.org/packages/c7/97/492ab10f9ac8695cd76b2fdb24e9e61f394051df71594e9bcc891c9f586e/pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7b93a4d08587e2b7e7882de461e82b6ed76d9026ce91ca7915e740ecc7855f60", size = 2067296, upload-time = "2025-11-04T13:42:10.817Z" }, { url = "https://files.pythonhosted.org/packages/ec/23/984149650e5269c59a2a4c41d234a9570adc68ab29981825cfaf4cfad8f4/pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8465ab91a4bd96d36dde3263f06caa6a8a6019e4113f24dc753d79a8b3a3f82", size = 2231548, upload-time = "2025-11-04T13:42:13.843Z" }, { url = "https://files.pythonhosted.org/packages/71/0c/85bcbb885b9732c28bec67a222dbed5ed2d77baee1f8bba2002e8cd00c5c/pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:299e0a22e7ae2b85c1a57f104538b2656e8ab1873511fd718a1c1c6f149b77b5", size = 2362571, upload-time = "2025-11-04T13:42:16.208Z" }, { url = "https://files.pythonhosted.org/packages/c0/4a/412d2048be12c334003e9b823a3fa3d038e46cc2d64dd8aab50b31b65499/pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:707625ef0983fcfb461acfaf14de2067c5942c6bb0f3b4c99158bed6fedd3cf3", size = 2068175, upload-time = "2025-11-04T13:42:18.911Z" }, { url = "https://files.pythonhosted.org/packages/73/f4/c58b6a776b502d0a5540ad02e232514285513572060f0d78f7832ca3c98b/pydantic_core-2.41.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f41eb9797986d6ebac5e8edff36d5cef9de40def462311b3eb3eeded1431e425", size = 2177203, upload-time = "2025-11-04T13:42:22.578Z" }, { url = "https://files.pythonhosted.org/packages/ed/ae/f06ea4c7e7a9eead3d165e7623cd2ea0cb788e277e4f935af63fc98fa4e6/pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0384e2e1021894b1ff5a786dbf94771e2986ebe2869533874d7e43bc79c6f504", size = 2148191, upload-time = "2025-11-04T13:42:24.89Z" }, { url = "https://files.pythonhosted.org/packages/c1/57/25a11dcdc656bf5f8b05902c3c2934ac3ea296257cc4a3f79a6319e61856/pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:f0cd744688278965817fd0839c4a4116add48d23890d468bc436f78beb28abf5", size = 2343907, upload-time = "2025-11-04T13:42:27.683Z" }, { url = "https://files.pythonhosted.org/packages/96/82/e33d5f4933d7a03327c0c43c65d575e5919d4974ffc026bc917a5f7b9f61/pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:753e230374206729bf0a807954bcc6c150d3743928a73faffee51ac6557a03c3", size = 2322174, upload-time = "2025-11-04T13:42:30.776Z" }, { url = "https://files.pythonhosted.org/packages/81/45/4091be67ce9f469e81656f880f3506f6a5624121ec5eb3eab37d7581897d/pydantic_core-2.41.5-cp39-cp39-win32.whl", hash = "sha256:873e0d5b4fb9b89ef7c2d2a963ea7d02879d9da0da8d9d4933dee8ee86a8b460", size = 1990353, upload-time = "2025-11-04T13:42:33.111Z" }, { url = "https://files.pythonhosted.org/packages/44/8a/a98aede18db6e9cd5d66bcacd8a409fcf8134204cdede2e7de35c5a2c5ef/pydantic_core-2.41.5-cp39-cp39-win_amd64.whl", hash = "sha256:e4f4a984405e91527a0d62649ee21138f8e3d0ef103be488c1dc11a80d7f184b", size = 2015698, upload-time = "2025-11-04T13:42:35.484Z" }, { url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441, upload-time = "2025-11-04T13:42:39.557Z" }, { url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291, upload-time = "2025-11-04T13:42:42.169Z" }, { url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632, upload-time = "2025-11-04T13:42:44.564Z" }, { url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905, upload-time = "2025-11-04T13:42:47.156Z" }, { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, { url = "https://files.pythonhosted.org/packages/e6/b0/1a2aa41e3b5a4ba11420aba2d091b2d17959c8d1519ece3627c371951e73/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8", size = 2103351, upload-time = "2025-11-04T13:43:02.058Z" }, { url = "https://files.pythonhosted.org/packages/a4/ee/31b1f0020baaf6d091c87900ae05c6aeae101fa4e188e1613c80e4f1ea31/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a", size = 1925363, upload-time = "2025-11-04T13:43:05.159Z" }, { url = "https://files.pythonhosted.org/packages/e1/89/ab8e86208467e467a80deaca4e434adac37b10a9d134cd2f99b28a01e483/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b", size = 2135615, upload-time = "2025-11-04T13:43:08.116Z" }, { url = "https://files.pythonhosted.org/packages/99/0a/99a53d06dd0348b2008f2f30884b34719c323f16c3be4e6cc1203b74a91d/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2", size = 2175369, upload-time = "2025-11-04T13:43:12.49Z" }, { url = "https://files.pythonhosted.org/packages/6d/94/30ca3b73c6d485b9bb0bc66e611cff4a7138ff9736b7e66bcf0852151636/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093", size = 2144218, upload-time = "2025-11-04T13:43:15.431Z" }, { url = "https://files.pythonhosted.org/packages/87/57/31b4f8e12680b739a91f472b5671294236b82586889ef764b5fbc6669238/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a", size = 2329951, upload-time = "2025-11-04T13:43:18.062Z" }, { url = "https://files.pythonhosted.org/packages/7d/73/3c2c8edef77b8f7310e6fb012dbc4b8551386ed575b9eb6fb2506e28a7eb/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963", size = 2318428, upload-time = "2025-11-04T13:43:20.679Z" }, { url = "https://files.pythonhosted.org/packages/2f/02/8559b1f26ee0d502c74f9cca5c0d2fd97e967e083e006bbbb4e97f3a043a/pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a", size = 2147009, upload-time = "2025-11-04T13:43:23.286Z" }, { url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980, upload-time = "2025-11-04T13:43:25.97Z" }, { url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865, upload-time = "2025-11-04T13:43:28.763Z" }, { url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256, upload-time = "2025-11-04T13:43:31.71Z" }, { url = "https://files.pythonhosted.org/packages/83/d9/8412d7f06f616bbc053d30cb4e5f76786af3221462ad5eee1f202021eb4e/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", size = 2174762, upload-time = "2025-11-04T13:43:34.744Z" }, { url = "https://files.pythonhosted.org/packages/55/4c/162d906b8e3ba3a99354e20faa1b49a85206c47de97a639510a0e673f5da/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", size = 2143141, upload-time = "2025-11-04T13:43:37.701Z" }, { url = "https://files.pythonhosted.org/packages/1f/f2/f11dd73284122713f5f89fc940f370d035fa8e1e078d446b3313955157fe/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", size = 2330317, upload-time = "2025-11-04T13:43:40.406Z" }, { url = "https://files.pythonhosted.org/packages/88/9d/b06ca6acfe4abb296110fb1273a4d848a0bfb2ff65f3ee92127b3244e16b/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", size = 2316992, upload-time = "2025-11-04T13:43:43.602Z" }, { url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302, upload-time = "2025-11-04T13:43:46.64Z" }, ] [[package]] name = "pydantic-settings" version = "2.13.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic", version = "2.12.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "python-dotenv", marker = "python_full_version >= '3.10'" }, { name = "typing-inspection", marker = "python_full_version >= '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/52/6d/fffca34caecc4a3f97bda81b2098da5e8ab7efc9a66e819074a11955d87e/pydantic_settings-2.13.1.tar.gz", hash = "sha256:b4c11847b15237fb0171e1462bf540e294affb9b86db4d9aa5c01730bdbe4025", size = 223826, upload-time = "2026-02-19T13:45:08.055Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/00/4b/ccc026168948fec4f7555b9164c724cf4125eac006e176541483d2c959be/pydantic_settings-2.13.1-py3-none-any.whl", hash = "sha256:d56fd801823dbeae7f0975e1f8c8e25c258eb75d278ea7abb5d9cebb01b56237", size = 58929, upload-time = "2026-02-19T13:45:06.034Z" }, ] [[package]] name = "pygments" version = "2.19.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, ] [[package]] name = "pyjwt" version = "2.12.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions", marker = "python_full_version == '3.10.*'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c2/27/a3b6e5bf6ff856d2509292e95c8f57f0df7017cf5394921fc4e4ef40308a/pyjwt-2.12.1.tar.gz", hash = "sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b", size = 102564, upload-time = "2026-03-13T19:27:37.25Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/e5/7a/8dd906bd22e79e47397a61742927f6747fe93242ef86645ee9092e610244/pyjwt-2.12.1-py3-none-any.whl", hash = "sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c", size = 29726, upload-time = "2026-03-13T19:27:35.677Z" }, ] [package.optional-dependencies] crypto = [ { name = "cryptography", marker = "python_full_version >= '3.10'" }, ] [[package]] name = "pyright" version = "1.1.399" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nodeenv" }, { name = "typing-extensions" }, ] sdist = { url = "https://files.pythonhosted.org/packages/db/9d/d91d5f6d26b2db95476fefc772e2b9a16d54c6bd0ea6bb5c1b6d635ab8b4/pyright-1.1.399.tar.gz", hash = "sha256:439035d707a36c3d1b443aec980bc37053fbda88158eded24b8eedcf1c7b7a1b", size = 3856954, upload-time = "2025-04-10T04:40:25.703Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/2f/b5/380380c9e7a534cb1783c70c3e8ac6d1193c599650a55838d0557586796e/pyright-1.1.399-py3-none-any.whl", hash = "sha256:55f9a875ddf23c9698f24208c764465ffdfd38be6265f7faf9a176e1dc549f3b", size = 5592584, upload-time = "2025-04-10T04:40:23.502Z" }, ] [[package]] name = "pytest" version = "8.4.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ { name = "colorama", marker = "(python_full_version < '3.10' and sys_platform == 'win32') or (python_full_version >= '3.10' and extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (python_full_version >= '3.10' and extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2') or (sys_platform != 'win32' and extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (sys_platform != 'win32' and extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "exceptiongroup", marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "iniconfig", version = "2.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "packaging", marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "pluggy", marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "pygments", marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "tomli", marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, ] [[package]] name = "pytest" version = "9.0.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.14' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and python_full_version < '3.14' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and python_full_version < '3.14' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra != 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", ] dependencies = [ { name = "colorama", marker = "(python_full_version >= '3.10' and sys_platform == 'win32') or (python_full_version < '3.10' and extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (python_full_version < '3.10' and extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2') or (sys_platform != 'win32' and extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (sys_platform != 'win32' and extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "exceptiongroup", marker = "python_full_version == '3.10.*' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "iniconfig", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "packaging", marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "pluggy", marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "pygments", marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "tomli", marker = "python_full_version == '3.10.*' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, ] [[package]] name = "pytest-asyncio" version = "1.2.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ { name = "backports-asyncio-runner", marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "typing-extensions", marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/42/86/9e3c5f48f7b7b638b216e4b9e645f54d199d7abbbab7a64a13b4e12ba10f/pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57", size = 50119, upload-time = "2025-09-12T07:33:53.816Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/04/93/2fa34714b7a4ae72f2f8dad66ba17dd9a2c793220719e736dda28b7aec27/pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99", size = 15095, upload-time = "2025-09-12T07:33:52.639Z" }, ] [[package]] name = "pytest-asyncio" version = "1.3.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.14' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and python_full_version < '3.14' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and python_full_version < '3.14' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra != 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", ] dependencies = [ { name = "backports-asyncio-runner", marker = "python_full_version == '3.10.*' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "pytest", version = "9.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "typing-extensions", marker = "(python_full_version >= '3.10' and python_full_version < '3.13') or (python_full_version < '3.10' and extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (python_full_version < '3.10' and extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2') or (python_full_version >= '3.13' and extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (python_full_version >= '3.13' and extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" }, ] [[package]] name = "pytest-xdist" version = "3.8.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "execnet" }, { name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "pytest", version = "9.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/78/b4/439b179d1ff526791eb921115fca8e44e596a13efeda518b9d845a619450/pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1", size = 88069, upload-time = "2025-07-01T13:30:59.346Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88", size = 46396, upload-time = "2025-07-01T13:30:56.632Z" }, ] [[package]] name = "python-dateutil" version = "2.9.0.post0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "six" }, ] sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, ] [[package]] name = "python-dotenv" version = "1.2.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/82/ed/0301aeeac3e5353ef3d94b6ec08bbcabd04a72018415dcb29e588514bba8/python_dotenv-1.2.2.tar.gz", hash = "sha256:2c371a91fbd7ba082c2c1dc1f8bf89ca22564a087c2c287cd9b662adde799cf3", size = 50135, upload-time = "2026-03-01T16:00:26.196Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/0b/d7/1959b9648791274998a9c3526f6d0ec8fd2233e4d4acce81bbae76b44b2a/python_dotenv-1.2.2-py3-none-any.whl", hash = "sha256:1d8214789a24de455a8b8bd8ae6fe3c6b69a5e3d64aa8a8e5d68e694bbcb285a", size = 22101, upload-time = "2026-03-01T16:00:25.09Z" }, ] [[package]] name = "python-multipart" version = "0.0.22" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/94/01/979e98d542a70714b0cb2b6728ed0b7c46792b695e3eaec3e20711271ca3/python_multipart-0.0.22.tar.gz", hash = "sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58", size = 37612, upload-time = "2026-01-25T10:15:56.219Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/1b/d0/397f9626e711ff749a95d96b7af99b9c566a9bb5129b8e4c10fc4d100304/python_multipart-0.0.22-py3-none-any.whl", hash = "sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155", size = 24579, upload-time = "2026-01-25T10:15:54.811Z" }, ] [[package]] name = "pywin32" version = "311" source = { registry = "https://pypi.org/simple" } wheels = [ { url = "https://files.pythonhosted.org/packages/7b/40/44efbb0dfbd33aca6a6483191dae0716070ed99e2ecb0c53683f400a0b4f/pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3", size = 8760432, upload-time = "2025-07-14T20:13:05.9Z" }, { url = "https://files.pythonhosted.org/packages/5e/bf/360243b1e953bd254a82f12653974be395ba880e7ec23e3731d9f73921cc/pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b", size = 9590103, upload-time = "2025-07-14T20:13:07.698Z" }, { url = "https://files.pythonhosted.org/packages/57/38/d290720e6f138086fb3d5ffe0b6caa019a791dd57866940c82e4eeaf2012/pywin32-311-cp310-cp310-win_arm64.whl", hash = "sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b", size = 8778557, upload-time = "2025-07-14T20:13:11.11Z" }, { url = "https://files.pythonhosted.org/packages/7c/af/449a6a91e5d6db51420875c54f6aff7c97a86a3b13a0b4f1a5c13b988de3/pywin32-311-cp311-cp311-win32.whl", hash = "sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151", size = 8697031, upload-time = "2025-07-14T20:13:13.266Z" }, { url = "https://files.pythonhosted.org/packages/51/8f/9bb81dd5bb77d22243d33c8397f09377056d5c687aa6d4042bea7fbf8364/pywin32-311-cp311-cp311-win_amd64.whl", hash = "sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503", size = 9508308, upload-time = "2025-07-14T20:13:15.147Z" }, { url = "https://files.pythonhosted.org/packages/44/7b/9c2ab54f74a138c491aba1b1cd0795ba61f144c711daea84a88b63dc0f6c/pywin32-311-cp311-cp311-win_arm64.whl", hash = "sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2", size = 8703930, upload-time = "2025-07-14T20:13:16.945Z" }, { url = "https://files.pythonhosted.org/packages/e7/ab/01ea1943d4eba0f850c3c61e78e8dd59757ff815ff3ccd0a84de5f541f42/pywin32-311-cp312-cp312-win32.whl", hash = "sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31", size = 8706543, upload-time = "2025-07-14T20:13:20.765Z" }, { url = "https://files.pythonhosted.org/packages/d1/a8/a0e8d07d4d051ec7502cd58b291ec98dcc0c3fff027caad0470b72cfcc2f/pywin32-311-cp312-cp312-win_amd64.whl", hash = "sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067", size = 9495040, upload-time = "2025-07-14T20:13:22.543Z" }, { url = "https://files.pythonhosted.org/packages/ba/3a/2ae996277b4b50f17d61f0603efd8253cb2d79cc7ae159468007b586396d/pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852", size = 8710102, upload-time = "2025-07-14T20:13:24.682Z" }, { url = "https://files.pythonhosted.org/packages/a5/be/3fd5de0979fcb3994bfee0d65ed8ca9506a8a1260651b86174f6a86f52b3/pywin32-311-cp313-cp313-win32.whl", hash = "sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d", size = 8705700, upload-time = "2025-07-14T20:13:26.471Z" }, { url = "https://files.pythonhosted.org/packages/e3/28/e0a1909523c6890208295a29e05c2adb2126364e289826c0a8bc7297bd5c/pywin32-311-cp313-cp313-win_amd64.whl", hash = "sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d", size = 9494700, upload-time = "2025-07-14T20:13:28.243Z" }, { url = "https://files.pythonhosted.org/packages/04/bf/90339ac0f55726dce7d794e6d79a18a91265bdf3aa70b6b9ca52f35e022a/pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a", size = 8709318, upload-time = "2025-07-14T20:13:30.348Z" }, { url = "https://files.pythonhosted.org/packages/c9/31/097f2e132c4f16d99a22bfb777e0fd88bd8e1c634304e102f313af69ace5/pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee", size = 8840714, upload-time = "2025-07-14T20:13:32.449Z" }, { url = "https://files.pythonhosted.org/packages/90/4b/07c77d8ba0e01349358082713400435347df8426208171ce297da32c313d/pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87", size = 9656800, upload-time = "2025-07-14T20:13:34.312Z" }, { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" }, { url = "https://files.pythonhosted.org/packages/59/42/b86689aac0cdaee7ae1c58d464b0ff04ca909c19bb6502d4973cdd9f9544/pywin32-311-cp39-cp39-win32.whl", hash = "sha256:aba8f82d551a942cb20d4a83413ccbac30790b50efb89a75e4f586ac0bb8056b", size = 8760837, upload-time = "2025-07-14T20:12:59.59Z" }, { url = "https://files.pythonhosted.org/packages/9f/8a/1403d0353f8c5a2f0829d2b1c4becbf9da2f0a4d040886404fc4a5431e4d/pywin32-311-cp39-cp39-win_amd64.whl", hash = "sha256:e0c4cfb0621281fe40387df582097fd796e80430597cb9944f0ae70447bacd91", size = 9590187, upload-time = "2025-07-14T20:13:01.419Z" }, { url = "https://files.pythonhosted.org/packages/60/22/e0e8d802f124772cec9c75430b01a212f86f9de7546bda715e54140d5aeb/pywin32-311-cp39-cp39-win_arm64.whl", hash = "sha256:62ea666235135fee79bb154e695f3ff67370afefd71bd7fea7512fc70ef31e3d", size = 8778162, upload-time = "2025-07-14T20:13:03.544Z" }, ] [[package]] name = "referencing" version = "0.37.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs", marker = "python_full_version >= '3.10'" }, { name = "rpds-py", marker = "python_full_version >= '3.10'" }, { name = "typing-extensions", marker = "python_full_version >= '3.10' and 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" } wheels = [ { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, ] [[package]] name = "requests" version = "2.32.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "charset-normalizer" }, { name = "idna" }, { name = "urllib3", version = "1.26.20", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "urllib3", version = "2.6.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, ] [[package]] name = "respx" version = "0.22.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f4/7c/96bd0bc759cf009675ad1ee1f96535edcb11e9666b985717eb8c87192a95/respx-0.22.0.tar.gz", hash = "sha256:3c8924caa2a50bd71aefc07aa812f2466ff489f1848c96e954a5362d17095d91", size = 28439, upload-time = "2024-12-19T22:33:59.374Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/8e/67/afbb0978d5399bc9ea200f1d4489a23c9a1dad4eee6376242b8182389c79/respx-0.22.0-py2.py3-none-any.whl", hash = "sha256:631128d4c9aba15e56903fb5f66fb1eff412ce28dd387ca3a81339e52dbd3ad0", size = 25127, upload-time = "2024-12-19T22:33:57.837Z" }, ] [[package]] name = "rich" version = "14.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown-it-py", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "markdown-it-py", version = "4.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "pygments" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b3/c6/f3b320c27991c46f43ee9d856302c70dc2d0fb2dba4842ff739d5f46b393/rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b", size = 230582, upload-time = "2026-02-19T17:23:12.474Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", size = 310458, upload-time = "2026-02-19T17:23:13.732Z" }, ] [[package]] name = "rpds-py" version = "0.30.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/06/0c/0c411a0ec64ccb6d104dcabe0e713e05e153a9a2c3c2bd2b32ce412166fe/rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288", size = 370490, upload-time = "2025-11-30T20:21:33.256Z" }, { url = "https://files.pythonhosted.org/packages/19/6a/4ba3d0fb7297ebae71171822554abe48d7cab29c28b8f9f2c04b79988c05/rpds_py-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00", size = 359751, upload-time = "2025-11-30T20:21:34.591Z" }, { url = "https://files.pythonhosted.org/packages/cd/7c/e4933565ef7f7a0818985d87c15d9d273f1a649afa6a52ea35ad011195ea/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6", size = 389696, upload-time = "2025-11-30T20:21:36.122Z" }, { url = "https://files.pythonhosted.org/packages/5e/01/6271a2511ad0815f00f7ed4390cf2567bec1d4b1da39e2c27a41e6e3b4de/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7", size = 403136, upload-time = "2025-11-30T20:21:37.728Z" }, { url = "https://files.pythonhosted.org/packages/55/64/c857eb7cd7541e9b4eee9d49c196e833128a55b89a9850a9c9ac33ccf897/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324", size = 524699, upload-time = "2025-11-30T20:21:38.92Z" }, { url = "https://files.pythonhosted.org/packages/9c/ed/94816543404078af9ab26159c44f9e98e20fe47e2126d5d32c9d9948d10a/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df", size = 412022, upload-time = "2025-11-30T20:21:40.407Z" }, { url = "https://files.pythonhosted.org/packages/61/b5/707f6cf0066a6412aacc11d17920ea2e19e5b2f04081c64526eb35b5c6e7/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3", size = 390522, upload-time = "2025-11-30T20:21:42.17Z" }, { url = "https://files.pythonhosted.org/packages/13/4e/57a85fda37a229ff4226f8cbcf09f2a455d1ed20e802ce5b2b4a7f5ed053/rpds_py-0.30.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221", size = 404579, upload-time = "2025-11-30T20:21:43.769Z" }, { url = "https://files.pythonhosted.org/packages/f9/da/c9339293513ec680a721e0e16bf2bac3db6e5d7e922488de471308349bba/rpds_py-0.30.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7", size = 421305, upload-time = "2025-11-30T20:21:44.994Z" }, { url = "https://files.pythonhosted.org/packages/f9/be/522cb84751114f4ad9d822ff5a1aa3c98006341895d5f084779b99596e5c/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff", size = 572503, upload-time = "2025-11-30T20:21:46.91Z" }, { url = "https://files.pythonhosted.org/packages/a2/9b/de879f7e7ceddc973ea6e4629e9b380213a6938a249e94b0cdbcc325bb66/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7", size = 598322, upload-time = "2025-11-30T20:21:48.709Z" }, { url = "https://files.pythonhosted.org/packages/48/ac/f01fc22efec3f37d8a914fc1b2fb9bcafd56a299edbe96406f3053edea5a/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139", size = 560792, upload-time = "2025-11-30T20:21:50.024Z" }, { url = "https://files.pythonhosted.org/packages/e2/da/4e2b19d0f131f35b6146425f846563d0ce036763e38913d917187307a671/rpds_py-0.30.0-cp310-cp310-win32.whl", hash = "sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464", size = 221901, upload-time = "2025-11-30T20:21:51.32Z" }, { url = "https://files.pythonhosted.org/packages/96/cb/156d7a5cf4f78a7cc571465d8aec7a3c447c94f6749c5123f08438bcf7bc/rpds_py-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169", size = 235823, upload-time = "2025-11-30T20:21:52.505Z" }, { url = "https://files.pythonhosted.org/packages/4d/6e/f964e88b3d2abee2a82c1ac8366da848fce1c6d834dc2132c3fda3970290/rpds_py-0.30.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425", size = 370157, upload-time = "2025-11-30T20:21:53.789Z" }, { url = "https://files.pythonhosted.org/packages/94/ba/24e5ebb7c1c82e74c4e4f33b2112a5573ddc703915b13a073737b59b86e0/rpds_py-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d", size = 359676, upload-time = "2025-11-30T20:21:55.475Z" }, { url = "https://files.pythonhosted.org/packages/84/86/04dbba1b087227747d64d80c3b74df946b986c57af0a9f0c98726d4d7a3b/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4", size = 389938, upload-time = "2025-11-30T20:21:57.079Z" }, { url = "https://files.pythonhosted.org/packages/42/bb/1463f0b1722b7f45431bdd468301991d1328b16cffe0b1c2918eba2c4eee/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f", size = 402932, upload-time = "2025-11-30T20:21:58.47Z" }, { url = "https://files.pythonhosted.org/packages/99/ee/2520700a5c1f2d76631f948b0736cdf9b0acb25abd0ca8e889b5c62ac2e3/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4", size = 525830, upload-time = "2025-11-30T20:21:59.699Z" }, { url = "https://files.pythonhosted.org/packages/e0/ad/bd0331f740f5705cc555a5e17fdf334671262160270962e69a2bdef3bf76/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97", size = 412033, upload-time = "2025-11-30T20:22:00.991Z" }, { url = "https://files.pythonhosted.org/packages/f8/1e/372195d326549bb51f0ba0f2ecb9874579906b97e08880e7a65c3bef1a99/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89", size = 390828, upload-time = "2025-11-30T20:22:02.723Z" }, { url = "https://files.pythonhosted.org/packages/ab/2b/d88bb33294e3e0c76bc8f351a3721212713629ffca1700fa94979cb3eae8/rpds_py-0.30.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d", size = 404683, upload-time = "2025-11-30T20:22:04.367Z" }, { url = "https://files.pythonhosted.org/packages/50/32/c759a8d42bcb5289c1fac697cd92f6fe01a018dd937e62ae77e0e7f15702/rpds_py-0.30.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038", size = 421583, upload-time = "2025-11-30T20:22:05.814Z" }, { url = "https://files.pythonhosted.org/packages/2b/81/e729761dbd55ddf5d84ec4ff1f47857f4374b0f19bdabfcf929164da3e24/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7", size = 572496, upload-time = "2025-11-30T20:22:07.713Z" }, { url = "https://files.pythonhosted.org/packages/14/f6/69066a924c3557c9c30baa6ec3a0aa07526305684c6f86c696b08860726c/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed", size = 598669, upload-time = "2025-11-30T20:22:09.312Z" }, { url = "https://files.pythonhosted.org/packages/5f/48/905896b1eb8a05630d20333d1d8ffd162394127b74ce0b0784ae04498d32/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85", size = 561011, upload-time = "2025-11-30T20:22:11.309Z" }, { url = "https://files.pythonhosted.org/packages/22/16/cd3027c7e279d22e5eb431dd3c0fbc677bed58797fe7581e148f3f68818b/rpds_py-0.30.0-cp311-cp311-win32.whl", hash = "sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c", size = 221406, upload-time = "2025-11-30T20:22:13.101Z" }, { url = "https://files.pythonhosted.org/packages/fa/5b/e7b7aa136f28462b344e652ee010d4de26ee9fd16f1bfd5811f5153ccf89/rpds_py-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825", size = 236024, upload-time = "2025-11-30T20:22:14.853Z" }, { url = "https://files.pythonhosted.org/packages/14/a6/364bba985e4c13658edb156640608f2c9e1d3ea3c81b27aa9d889fff0e31/rpds_py-0.30.0-cp311-cp311-win_arm64.whl", hash = "sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229", size = 229069, upload-time = "2025-11-30T20:22:16.577Z" }, { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086, upload-time = "2025-11-30T20:22:17.93Z" }, { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053, upload-time = "2025-11-30T20:22:19.297Z" }, { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763, upload-time = "2025-11-30T20:22:21.661Z" }, { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951, upload-time = "2025-11-30T20:22:23.408Z" }, { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622, upload-time = "2025-11-30T20:22:25.16Z" }, { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492, upload-time = "2025-11-30T20:22:26.505Z" }, { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080, upload-time = "2025-11-30T20:22:27.934Z" }, { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680, upload-time = "2025-11-30T20:22:29.341Z" }, { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589, upload-time = "2025-11-30T20:22:31.469Z" }, { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289, upload-time = "2025-11-30T20:22:32.997Z" }, { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737, upload-time = "2025-11-30T20:22:34.419Z" }, { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120, upload-time = "2025-11-30T20:22:35.903Z" }, { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782, upload-time = "2025-11-30T20:22:37.271Z" }, { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463, upload-time = "2025-11-30T20:22:39.021Z" }, { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868, upload-time = "2025-11-30T20:22:40.493Z" }, { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887, upload-time = "2025-11-30T20:22:41.812Z" }, { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904, upload-time = "2025-11-30T20:22:43.479Z" }, { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945, upload-time = "2025-11-30T20:22:44.819Z" }, { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783, upload-time = "2025-11-30T20:22:46.103Z" }, { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021, upload-time = "2025-11-30T20:22:47.458Z" }, { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589, upload-time = "2025-11-30T20:22:48.872Z" }, { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025, upload-time = "2025-11-30T20:22:50.196Z" }, { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895, upload-time = "2025-11-30T20:22:51.87Z" }, { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799, upload-time = "2025-11-30T20:22:53.341Z" }, { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731, upload-time = "2025-11-30T20:22:54.778Z" }, { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027, upload-time = "2025-11-30T20:22:56.212Z" }, { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020, upload-time = "2025-11-30T20:22:58.2Z" }, { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139, upload-time = "2025-11-30T20:23:00.209Z" }, { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224, upload-time = "2025-11-30T20:23:02.008Z" }, { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645, upload-time = "2025-11-30T20:23:03.43Z" }, { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443, upload-time = "2025-11-30T20:23:04.878Z" }, { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375, upload-time = "2025-11-30T20:23:06.342Z" }, { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850, upload-time = "2025-11-30T20:23:07.825Z" }, { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812, upload-time = "2025-11-30T20:23:09.228Z" }, { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841, upload-time = "2025-11-30T20:23:11.186Z" }, { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149, upload-time = "2025-11-30T20:23:12.864Z" }, { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843, upload-time = "2025-11-30T20:23:14.638Z" }, { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507, upload-time = "2025-11-30T20:23:16.105Z" }, { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949, upload-time = "2025-11-30T20:23:17.539Z" }, { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790, upload-time = "2025-11-30T20:23:19.029Z" }, { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217, upload-time = "2025-11-30T20:23:20.885Z" }, { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806, upload-time = "2025-11-30T20:23:22.488Z" }, { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341, upload-time = "2025-11-30T20:23:24.449Z" }, { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768, upload-time = "2025-11-30T20:23:25.908Z" }, { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, { url = "https://files.pythonhosted.org/packages/69/71/3f34339ee70521864411f8b6992e7ab13ac30d8e4e3309e07c7361767d91/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58", size = 372292, upload-time = "2025-11-30T20:24:16.537Z" }, { url = "https://files.pythonhosted.org/packages/57/09/f183df9b8f2d66720d2ef71075c59f7e1b336bec7ee4c48f0a2b06857653/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a", size = 362128, upload-time = "2025-11-30T20:24:18.086Z" }, { url = "https://files.pythonhosted.org/packages/7a/68/5c2594e937253457342e078f0cc1ded3dd7b2ad59afdbf2d354869110a02/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb", size = 391542, upload-time = "2025-11-30T20:24:20.092Z" }, { url = "https://files.pythonhosted.org/packages/49/5c/31ef1afd70b4b4fbdb2800249f34c57c64beb687495b10aec0365f53dfc4/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c", size = 404004, upload-time = "2025-11-30T20:24:22.231Z" }, { url = "https://files.pythonhosted.org/packages/e3/63/0cfbea38d05756f3440ce6534d51a491d26176ac045e2707adc99bb6e60a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3", size = 527063, upload-time = "2025-11-30T20:24:24.302Z" }, { url = "https://files.pythonhosted.org/packages/42/e6/01e1f72a2456678b0f618fc9a1a13f882061690893c192fcad9f2926553a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5", size = 413099, upload-time = "2025-11-30T20:24:25.916Z" }, { url = "https://files.pythonhosted.org/packages/b8/25/8df56677f209003dcbb180765520c544525e3ef21ea72279c98b9aa7c7fb/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738", size = 392177, upload-time = "2025-11-30T20:24:27.834Z" }, { url = "https://files.pythonhosted.org/packages/4a/b4/0a771378c5f16f8115f796d1f437950158679bcd2a7c68cf251cfb00ed5b/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f", size = 406015, upload-time = "2025-11-30T20:24:29.457Z" }, { url = "https://files.pythonhosted.org/packages/36/d8/456dbba0af75049dc6f63ff295a2f92766b9d521fa00de67a2bd6427d57a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877", size = 423736, upload-time = "2025-11-30T20:24:31.22Z" }, { url = "https://files.pythonhosted.org/packages/13/64/b4d76f227d5c45a7e0b796c674fd81b0a6c4fbd48dc29271857d8219571c/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a", size = 573981, upload-time = "2025-11-30T20:24:32.934Z" }, { url = "https://files.pythonhosted.org/packages/20/91/092bacadeda3edf92bf743cc96a7be133e13a39cdbfd7b5082e7ab638406/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4", size = 599782, upload-time = "2025-11-30T20:24:35.169Z" }, { url = "https://files.pythonhosted.org/packages/d1/b7/b95708304cd49b7b6f82fdd039f1748b66ec2b21d6a45180910802f1abf1/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e", size = 562191, upload-time = "2025-11-30T20:24:36.853Z" }, ] [[package]] name = "ruff" version = "0.15.6" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/51/df/f8629c19c5318601d3121e230f74cbee7a3732339c52b21daa2b82ef9c7d/ruff-0.15.6.tar.gz", hash = "sha256:8394c7bb153a4e3811a4ecdacd4a8e6a4fa8097028119160dffecdcdf9b56ae4", size = 4597916, upload-time = "2026-03-12T23:05:47.51Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/9e/2f/4e03a7e5ce99b517e98d3b4951f411de2b0fa8348d39cf446671adcce9a2/ruff-0.15.6-py3-none-linux_armv6l.whl", hash = "sha256:7c98c3b16407b2cf3d0f2b80c80187384bc92c6774d85fefa913ecd941256fff", size = 10508953, upload-time = "2026-03-12T23:05:17.246Z" }, { url = "https://files.pythonhosted.org/packages/70/60/55bcdc3e9f80bcf39edf0cd272da6fa511a3d94d5a0dd9e0adf76ceebdb4/ruff-0.15.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ee7dcfaad8b282a284df4aa6ddc2741b3f4a18b0555d626805555a820ea181c3", size = 10942257, upload-time = "2026-03-12T23:05:23.076Z" }, { url = "https://files.pythonhosted.org/packages/e7/f9/005c29bd1726c0f492bfa215e95154cf480574140cb5f867c797c18c790b/ruff-0.15.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3bd9967851a25f038fc8b9ae88a7fbd1b609f30349231dffaa37b6804923c4bb", size = 10322683, upload-time = "2026-03-12T23:05:33.738Z" }, { url = "https://files.pythonhosted.org/packages/5f/74/2f861f5fd7cbb2146bddb5501450300ce41562da36d21868c69b7a828169/ruff-0.15.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13f4594b04e42cd24a41da653886b04d2ff87adbf57497ed4f728b0e8a4866f8", size = 10660986, upload-time = "2026-03-12T23:05:53.245Z" }, { url = "https://files.pythonhosted.org/packages/c1/a1/309f2364a424eccb763cdafc49df843c282609f47fe53aa83f38272389e0/ruff-0.15.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e2ed8aea2f3fe57886d3f00ea5b8aae5bf68d5e195f487f037a955ff9fbaac9e", size = 10332177, upload-time = "2026-03-12T23:05:56.145Z" }, { url = "https://files.pythonhosted.org/packages/30/41/7ebf1d32658b4bab20f8ac80972fb19cd4e2c6b78552be263a680edc55ac/ruff-0.15.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70789d3e7830b848b548aae96766431c0dc01a6c78c13381f423bf7076c66d15", size = 11170783, upload-time = "2026-03-12T23:06:01.742Z" }, { url = "https://files.pythonhosted.org/packages/76/be/6d488f6adca047df82cd62c304638bcb00821c36bd4881cfca221561fdfc/ruff-0.15.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:542aaf1de3154cea088ced5a819ce872611256ffe2498e750bbae5247a8114e9", size = 12044201, upload-time = "2026-03-12T23:05:28.697Z" }, { url = "https://files.pythonhosted.org/packages/71/68/e6f125df4af7e6d0b498f8d373274794bc5156b324e8ab4bf5c1b4fc0ec7/ruff-0.15.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c22e6f02c16cfac3888aa636e9eba857254d15bbacc9906c9689fdecb1953ab", size = 11421561, upload-time = "2026-03-12T23:05:31.236Z" }, { url = "https://files.pythonhosted.org/packages/f1/9f/f85ef5fd01a52e0b472b26dc1b4bd228b8f6f0435975442ffa4741278703/ruff-0.15.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98893c4c0aadc8e448cfa315bd0cc343a5323d740fe5f28ef8a3f9e21b381f7e", size = 11310928, upload-time = "2026-03-12T23:05:45.288Z" }, { url = "https://files.pythonhosted.org/packages/8c/26/b75f8c421f5654304b89471ed384ae8c7f42b4dff58fa6ce1626d7f2b59a/ruff-0.15.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:70d263770d234912374493e8cc1e7385c5d49376e41dfa51c5c3453169dc581c", size = 11235186, upload-time = "2026-03-12T23:05:50.677Z" }, { url = "https://files.pythonhosted.org/packages/fc/d4/d5a6d065962ff7a68a86c9b4f5500f7d101a0792078de636526c0edd40da/ruff-0.15.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:55a1ad63c5a6e54b1f21b7514dfadc0c7fb40093fa22e95143cf3f64ebdcd512", size = 10635231, upload-time = "2026-03-12T23:05:37.044Z" }, { url = "https://files.pythonhosted.org/packages/d6/56/7c3acf3d50910375349016cf33de24be021532042afbed87942858992491/ruff-0.15.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8dc473ba093c5ec238bb1e7429ee676dca24643c471e11fbaa8a857925b061c0", size = 10340357, upload-time = "2026-03-12T23:06:04.748Z" }, { url = "https://files.pythonhosted.org/packages/06/54/6faa39e9c1033ff6a3b6e76b5df536931cd30caf64988e112bbf91ef5ce5/ruff-0.15.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:85b042377c2a5561131767974617006f99f7e13c63c111b998f29fc1e58a4cfb", size = 10860583, upload-time = "2026-03-12T23:05:58.978Z" }, { url = "https://files.pythonhosted.org/packages/cb/1e/509a201b843b4dfb0b32acdedf68d951d3377988cae43949ba4c4133a96a/ruff-0.15.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:cef49e30bc5a86a6a92098a7fbf6e467a234d90b63305d6f3ec01225a9d092e0", size = 11410976, upload-time = "2026-03-12T23:05:39.955Z" }, { url = "https://files.pythonhosted.org/packages/6c/25/3fc9114abf979a41673ce877c08016f8e660ad6cf508c3957f537d2e9fa9/ruff-0.15.6-py3-none-win32.whl", hash = "sha256:bbf67d39832404812a2d23020dda68fee7f18ce15654e96fb1d3ad21a5fe436c", size = 10616872, upload-time = "2026-03-12T23:05:42.451Z" }, { url = "https://files.pythonhosted.org/packages/89/7a/09ece68445ceac348df06e08bf75db72d0e8427765b96c9c0ffabc1be1d9/ruff-0.15.6-py3-none-win_amd64.whl", hash = "sha256:aee25bc84c2f1007ecb5037dff75cef00414fdf17c23f07dc13e577883dca406", size = 11787271, upload-time = "2026-03-12T23:05:20.168Z" }, { url = "https://files.pythonhosted.org/packages/7f/d0/578c47dd68152ddddddf31cd7fc67dc30b7cdf639a86275fda821b0d9d98/ruff-0.15.6-py3-none-win_arm64.whl", hash = "sha256:c34de3dd0b0ba203be50ae70f5910b17188556630e2178fd7d79fc030eb0d837", size = 11060497, upload-time = "2026-03-12T23:05:25.968Z" }, ] [[package]] name = "s3transfer" version = "0.16.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, ] sdist = { url = "https://files.pythonhosted.org/packages/05/04/74127fc843314818edfa81b5540e26dd537353b123a4edc563109d8f17dd/s3transfer-0.16.0.tar.gz", hash = "sha256:8e990f13268025792229cd52fa10cb7163744bf56e719e0b9cb925ab79abf920", size = 153827, upload-time = "2025-12-01T02:30:59.114Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/fc/51/727abb13f44c1fcf6d145979e1535a35794db0f6e450a0cb46aa24732fe2/s3transfer-0.16.0-py3-none-any.whl", hash = "sha256:18e25d66fed509e3868dc1572b3f427ff947dd2c56f844a5bf09481ad3f3b2fe", size = 86830, upload-time = "2025-12-01T02:30:57.729Z" }, ] [[package]] name = "six" version = "1.17.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] [[package]] name = "sniffio" version = "1.3.1" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, ] [[package]] name = "sse-starlette" version = "3.3.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio", marker = "python_full_version >= '3.10'" }, { name = "starlette", marker = "python_full_version >= '3.10'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5a/9f/c3695c2d2d4ef70072c3a06992850498b01c6bc9be531950813716b426fa/sse_starlette-3.3.2.tar.gz", hash = "sha256:678fca55a1945c734d8472a6cad186a55ab02840b4f6786f5ee8770970579dcd", size = 32326, upload-time = "2026-02-28T11:24:34.36Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/61/28/8cb142d3fe80c4a2d8af54ca0b003f47ce0ba920974e7990fa6e016402d1/sse_starlette-3.3.2-py3-none-any.whl", hash = "sha256:5c3ea3dad425c601236726af2f27689b74494643f57017cafcb6f8c9acfbb862", size = 14270, upload-time = "2026-02-28T11:24:32.984Z" }, ] [[package]] name = "standardwebhooks" version = "1.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, { name = "deprecated" }, { name = "httpx" }, { name = "python-dateutil" }, { name = "types-deprecated", version = "1.3.1.20260130", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "types-deprecated", version = "1.3.1.20260408", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "types-python-dateutil", version = "2.9.0.20260124", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "types-python-dateutil", version = "2.9.0.20260408", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c4/7d/04fc3aa177403472d3ddae90953d8f878dc5fd21ba29c02fc9e97e10703f/standardwebhooks-1.0.1.tar.gz", hash = "sha256:b557bb2e4b16ada179a517ec0fe6cbec5acf976c5619922bf29c457f89a451bd", size = 5103, upload-time = "2026-02-18T19:13:06.793Z" } [[package]] name = "starlette" version = "0.52.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio", marker = "python_full_version >= '3.10'" }, { name = "typing-extensions", marker = "python_full_version >= '3.10' and 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" } 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" }, ] [[package]] name = "time-machine" version = "2.19.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ { name = "python-dateutil", marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f8/a4/1b5fdd165f61b67f445fac2a7feb0c655118edef429cd09ff5a8067f7f1d/time_machine-2.19.0.tar.gz", hash = "sha256:7c5065a8b3f2bbb449422c66ef71d114d3f909c276a6469642ecfffb6a0fcd29", size = 14576, upload-time = "2025-08-19T17:22:08.402Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/9d/8f/19125611ebbcb3a14da14cd982b9eb4573e2733db60c9f1fbf6a39534f40/time_machine-2.19.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b5169018ef47206997b46086ce01881cd3a4666fd2998c9d76a87858ca3e49e9", size = 19659, upload-time = "2025-08-19T17:20:30.062Z" }, { url = "https://files.pythonhosted.org/packages/74/da/9b0a928321e7822a3ff96dbd1eae089883848e30e9e1b149b85fb96ba56b/time_machine-2.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:85bb7ed440fccf6f6d0c8f7d68d849e7c3d1f771d5e0b2cdf871fa6561da569f", size = 15157, upload-time = "2025-08-19T17:20:31.931Z" }, { url = "https://files.pythonhosted.org/packages/36/ff/d7e943422038f5f2161fe2c2d791e64a45be691ef946020b20f3a6efc4d4/time_machine-2.19.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a3b12028af1cdc09ccd595be2168b7b26f206c1e190090b048598fbe278beb8e", size = 32860, upload-time = "2025-08-19T17:20:33.241Z" }, { url = "https://files.pythonhosted.org/packages/fc/80/2b0f1070ed9808ee7da7a6da62a4a0b776957cb4d861578348f86446e778/time_machine-2.19.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c261f073086cf081d1443cbf7684148c662659d3d139d06b772bfe3fe7cc71a6", size = 34510, upload-time = "2025-08-19T17:20:34.221Z" }, { url = "https://files.pythonhosted.org/packages/ef/b4/48038691c8d89924b36c83335a73adeeb68c884f5a1da08a5b17b8a956f3/time_machine-2.19.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:011954d951230a9f1079f22b39ed1a3a9abb50ee297dfb8c557c46351659d94d", size = 36204, upload-time = "2025-08-19T17:20:35.163Z" }, { url = "https://files.pythonhosted.org/packages/37/2e/60e8adb541df195e83cb74b720b2cfb1f22ed99c5a7f8abf2a9ab3442cb5/time_machine-2.19.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b0f83308b29c7872006803f2e77318874eb84d0654f2afe0e48e3822e7a2e39b", size = 34936, upload-time = "2025-08-19T17:20:36.61Z" }, { url = "https://files.pythonhosted.org/packages/5e/72/e8cee59c6cd99dd3b25b8001a0253e779a286aa8f44d5b40777cbd66210b/time_machine-2.19.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:39733ef844e2984620ec9382a42d00cccc4757d75a5dd572be8c2572e86e50b9", size = 32932, upload-time = "2025-08-19T17:20:37.901Z" }, { url = "https://files.pythonhosted.org/packages/2c/eb/83f300d93c1504965d944e03679f1c943a923bce2d0fdfadef0e2e22cc13/time_machine-2.19.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f8db99f6334432e9ffbf00c215caf2ae9773f17cec08304d77e9e90febc3507b", size = 34010, upload-time = "2025-08-19T17:20:39.202Z" }, { url = "https://files.pythonhosted.org/packages/e1/77/f35f2500e04daac5033a22fbfd17e68467822b8406ee77966bf222ccaa26/time_machine-2.19.0-cp310-cp310-win32.whl", hash = "sha256:72bf66cd19e27ffd26516b9cbe676d50c2e0b026153289765dfe0cf406708128", size = 17121, upload-time = "2025-08-19T17:20:40.108Z" }, { url = "https://files.pythonhosted.org/packages/db/df/32d3e0404be1760a64a44caab2af34b07e952bfe00a23134fea9ddba3e8a/time_machine-2.19.0-cp310-cp310-win_amd64.whl", hash = "sha256:46f1c945934ce3d6b4f388b8e581fce7f87ec891ea90d7128e19520e434f96f0", size = 17957, upload-time = "2025-08-19T17:20:41.079Z" }, { url = "https://files.pythonhosted.org/packages/66/df/598a71a1afb4b509a4587273b76590b16d9110a3e9106f01eedc68d02bb2/time_machine-2.19.0-cp310-cp310-win_arm64.whl", hash = "sha256:fb4897c7a5120a4fd03f0670f332d83b7e55645886cd8864a71944c4c2e5b35b", size = 16821, upload-time = "2025-08-19T17:20:41.967Z" }, { url = "https://files.pythonhosted.org/packages/1d/ed/4815ebcc9b6c14273f692b9be38a9b09eae52a7e532407cc61a51912b121/time_machine-2.19.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5ee91664880434d98e41585c3446dac7180ec408c786347451ddfca110d19296", size = 19342, upload-time = "2025-08-19T17:20:43.207Z" }, { url = "https://files.pythonhosted.org/packages/ee/08/154cce8b11b60d8238b0b751b8901d369999f4e8f7c3a5f917caa5d95b0b/time_machine-2.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ed3732b83a893d1c7b8cabde762968b4dc5680ee0d305b3ecca9bb516f4e3862", size = 14978, upload-time = "2025-08-19T17:20:44.134Z" }, { url = "https://files.pythonhosted.org/packages/c7/b7/b689d8c8eeca7af375cfcd64973e49e83aa817cc00f80f98548d42c0eb50/time_machine-2.19.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6ba0303e9cc9f7f947e344f501e26bedfb68fab521e3c2729d370f4f332d2d55", size = 30964, upload-time = "2025-08-19T17:20:45.366Z" }, { url = "https://files.pythonhosted.org/packages/80/91/38bf9c79674e95ce32e23c267055f281dff651eec77ed32a677db3dc011a/time_machine-2.19.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2851825b524a988ee459c37c1c26bdfaa7eff78194efb2b562ea497a6f375b0a", size = 32606, upload-time = "2025-08-19T17:20:46.693Z" }, { url = "https://files.pythonhosted.org/packages/19/4a/e9222d85d4de68975a5e799f539a9d32f3a134a9101fca0a61fa6aa33d68/time_machine-2.19.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:68d32b09ecfd7fef59255c091e8e7c24dd117f882c4880b5c7ab8c5c32a98f89", size = 34405, upload-time = "2025-08-19T17:20:48.032Z" }, { url = "https://files.pythonhosted.org/packages/14/e2/09480d608d42d6876f9ff74593cfc9197a7eb2c31381a74fb2b145575b65/time_machine-2.19.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:60c46ab527bf2fa144b530f639cc9e12803524c9e1f111dc8c8f493bb6586eeb", size = 33181, upload-time = "2025-08-19T17:20:48.937Z" }, { url = "https://files.pythonhosted.org/packages/84/64/f9359e000fad32d9066305c48abc527241d608bcdf77c19d67d66e268455/time_machine-2.19.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:56f26ab9f0201c453d18fe76bb7d1cf05fe58c1b9d9cb0c7d243d05132e01292", size = 31036, upload-time = "2025-08-19T17:20:50.276Z" }, { url = "https://files.pythonhosted.org/packages/71/0d/fab2aacec71e3e482bd7fce0589381f9414a4a97f8766bddad04ad047b7b/time_machine-2.19.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6c806cf3c1185baa1d807b7f51bed0db7a6506832c961d5d1b4c94c775749bc0", size = 32145, upload-time = "2025-08-19T17:20:51.449Z" }, { url = "https://files.pythonhosted.org/packages/44/fb/faeba2405fb27553f7b28db441a500e2064ffdb2dcba001ee315fdd2c121/time_machine-2.19.0-cp311-cp311-win32.whl", hash = "sha256:b30039dfd89855c12138095bee39c540b4633cbc3684580d684ef67a99a91587", size = 17004, upload-time = "2025-08-19T17:20:52.38Z" }, { url = "https://files.pythonhosted.org/packages/2f/84/87e483d660ca669426192969280366635c845c3154a9fe750be546ed3afc/time_machine-2.19.0-cp311-cp311-win_amd64.whl", hash = "sha256:13ed8b34430f1de79905877f5600adffa626793ab4546a70a99fb72c6a3350d8", size = 17822, upload-time = "2025-08-19T17:20:53.348Z" }, { url = "https://files.pythonhosted.org/packages/41/f4/ebf7bbf5047854a528adaf54a5e8780bc5f7f0104c298ab44566a3053bf8/time_machine-2.19.0-cp311-cp311-win_arm64.whl", hash = "sha256:cc29a50a0257d8750b08056b66d7225daab47606832dea1a69e8b017323bf511", size = 16680, upload-time = "2025-08-19T17:20:54.26Z" }, { url = "https://files.pythonhosted.org/packages/9b/aa/7e00614d339e4d687f6e96e312a1566022528427d237ec639df66c4547bc/time_machine-2.19.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:c85cf437dc3c07429456d8d6670ac90ecbd8241dcd0fbf03e8db2800576f91ff", size = 19308, upload-time = "2025-08-19T17:20:55.25Z" }, { url = "https://files.pythonhosted.org/packages/ab/3c/bde3c757394f5bca2fbc1528d4117960a26c38f9b160bf471b38d2378d8f/time_machine-2.19.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d9238897e8ef54acdf59f5dff16f59ca0720e7c02d820c56b4397c11db5d3eb9", size = 15019, upload-time = "2025-08-19T17:20:56.204Z" }, { url = "https://files.pythonhosted.org/packages/c8/e0/8ca916dd918018352d377f1f5226ee071cfbeb7dbbde2b03d14a411ac2b1/time_machine-2.19.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e312c7d5d6bfffb96c6a7b39ff29e3046de100d7efaa3c01552654cfbd08f14c", size = 33079, upload-time = "2025-08-19T17:20:57.166Z" }, { url = "https://files.pythonhosted.org/packages/48/69/184a0209f02dd0cb5e01e8d13cd4c97a5f389c4e3d09b95160dd676ad1e7/time_machine-2.19.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:714c40b2c90d1c57cc403382d5a9cf16e504cb525bfe9650095317da3c3d62b5", size = 34925, upload-time = "2025-08-19T17:20:58.117Z" }, { url = "https://files.pythonhosted.org/packages/43/42/4bbf4309e8e57cea1086eb99052d97ff6ddecc1ab6a3b07aa4512f8bf963/time_machine-2.19.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2eaa1c675d500dc3ccae19e9fb1feff84458a68c132bbea47a80cc3dd2df7072", size = 36384, upload-time = "2025-08-19T17:20:59.108Z" }, { url = "https://files.pythonhosted.org/packages/b1/af/9f510dc1719157348c1a2e87423aed406589070b54b503cb237d9bf3a4fe/time_machine-2.19.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e77a414e9597988af53b2b2e67242c9d2f409769df0d264b6d06fda8ca3360d4", size = 34881, upload-time = "2025-08-19T17:21:00.116Z" }, { url = "https://files.pythonhosted.org/packages/ca/28/61764a635c70cc76c76ba582dfdc1a84834cddaeb96789023af5214426b2/time_machine-2.19.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cd93996970e11c382b04d4937c3cd0b0167adeef14725ece35aae88d8a01733c", size = 32931, upload-time = "2025-08-19T17:21:01.095Z" }, { url = "https://files.pythonhosted.org/packages/b6/e0/f028d93b266e6ade8aca5851f76ebbc605b2905cdc29981a2943b43e1a6c/time_machine-2.19.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8e20a6d8d6e23174bd7e931e134d9610b136db460b249d07e84ecdad029ec352", size = 34241, upload-time = "2025-08-19T17:21:02.052Z" }, { url = "https://files.pythonhosted.org/packages/7d/a6/36d1950ed1d3f613158024cf1dcc73db1d9ef0b9117cf51ef2e37dc06499/time_machine-2.19.0-cp312-cp312-win32.whl", hash = "sha256:95afc9bc65228b27be80c2756799c20b8eb97c4ef382a9b762b6d7888bc84099", size = 17021, upload-time = "2025-08-19T17:21:03.374Z" }, { url = "https://files.pythonhosted.org/packages/b1/0d/e2dce93355abda3cac69e77fe96566757e98b8fe7fdcbddce89c9ced3f5f/time_machine-2.19.0-cp312-cp312-win_amd64.whl", hash = "sha256:e84909af950e2448f4e2562ea5759c946248c99ab380d2b47d79b62bd76fa236", size = 17857, upload-time = "2025-08-19T17:21:04.331Z" }, { url = "https://files.pythonhosted.org/packages/eb/28/50ae6fb83b7feeeca7a461c0dc156cf7ef5e6ef594a600d06634fde6a2cb/time_machine-2.19.0-cp312-cp312-win_arm64.whl", hash = "sha256:0390a1ea9fa7e9d772a39b7c61b34fdcca80eb9ffac339cc0441c6c714c81470", size = 16677, upload-time = "2025-08-19T17:21:05.39Z" }, { url = "https://files.pythonhosted.org/packages/a9/b8/24ebce67aa531bae2cbe164bb3f4abc6467dc31f3aead35e77f5a075ea3e/time_machine-2.19.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5e172866753e6041d3b29f3037dc47c20525176a494a71bbd0998dfdc4f11f2f", size = 19373, upload-time = "2025-08-19T17:21:06.701Z" }, { url = "https://files.pythonhosted.org/packages/53/a5/c9a5240fd2f845d3ff9fa26f8c8eaa29f7239af9d65007e61d212250f15b/time_machine-2.19.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f70f68379bd6f542ae6775cce9a4fa3dcc20bf7959c42eaef871c14469e18097", size = 15056, upload-time = "2025-08-19T17:21:07.667Z" }, { url = "https://files.pythonhosted.org/packages/b9/92/66cce5d2fb2a5e68459aca85fd18a7e2d216f725988940cd83f96630f2f1/time_machine-2.19.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e69e0b0f694728a00e72891ef8dd00c7542952cb1c87237db594b6b27d504a96", size = 33172, upload-time = "2025-08-19T17:21:08.619Z" }, { url = "https://files.pythonhosted.org/packages/ae/20/b499e9ab4364cd466016c33dcdf4f56629ca4c20b865bd4196d229f31d92/time_machine-2.19.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3ae0a8b869574301ec5637e32c270c7384cca5cd6e230f07af9d29271a7fa293", size = 35042, upload-time = "2025-08-19T17:21:09.622Z" }, { url = "https://files.pythonhosted.org/packages/41/32/b252d3d32791eb16c07d553c820dbc33d9c7fa771de3d1c602190bded2b7/time_machine-2.19.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:554e4317de90e2f7605ff80d153c8bb56b38c0d0c0279feb17e799521e987b8c", size = 36535, upload-time = "2025-08-19T17:21:10.571Z" }, { url = "https://files.pythonhosted.org/packages/98/cf/4d0470062b9742e1b040ab81bad04d1a5d1de09806507bb6188989cfa1a7/time_machine-2.19.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6567a5ec5538ed550539ac29be11b3cb36af1f9894e2a72940cba0292cc7c3c9", size = 34945, upload-time = "2025-08-19T17:21:11.538Z" }, { url = "https://files.pythonhosted.org/packages/24/71/2f741b29d98b1c18f6777a32236497c3d3264b6077e431cea4695684c8a1/time_machine-2.19.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:82e9ffe8dfff07b0d810a2ad015a82cd78c6a237f6c7cf185fa7f747a3256f8a", size = 33014, upload-time = "2025-08-19T17:21:12.858Z" }, { url = "https://files.pythonhosted.org/packages/e8/83/ca8dba6106562843fd99f672e5aaf95badbc10f4f13f7cfe8d8640a7019d/time_machine-2.19.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7e1c4e578cdd69b3531d8dd3fbcb92a0cd879dadb912ee37af99c3a9e3c0d285", size = 34350, upload-time = "2025-08-19T17:21:13.923Z" }, { url = "https://files.pythonhosted.org/packages/21/7f/34fe540450e18d0a993240100e4b86e8d03d831b92af8bb6ddb2662dc6fc/time_machine-2.19.0-cp313-cp313-win32.whl", hash = "sha256:72dbd4cbc3d96dec9dd281ddfbb513982102776b63e4e039f83afb244802a9e5", size = 17047, upload-time = "2025-08-19T17:21:14.874Z" }, { url = "https://files.pythonhosted.org/packages/bf/5d/c8be73df82c7ebe7cd133279670e89b8b110af3ce1412c551caa9d08e625/time_machine-2.19.0-cp313-cp313-win_amd64.whl", hash = "sha256:e17e3e089ac95f9a145ce07ff615e3c85674f7de36f2d92aaf588493a23ffb4b", size = 17868, upload-time = "2025-08-19T17:21:15.819Z" }, { url = "https://files.pythonhosted.org/packages/92/13/2dfd3b8fb285308f61cd7aa9bfa96f46ddf916e3549a0f0afd094c556599/time_machine-2.19.0-cp313-cp313-win_arm64.whl", hash = "sha256:149072aff8e3690e14f4916103d898ea0d5d9c95531b6aa0995251c299533f7b", size = 16710, upload-time = "2025-08-19T17:21:16.748Z" }, { url = "https://files.pythonhosted.org/packages/05/c1/deebb361727d2c5790f9d4d874be1b19afd41f4375581df465e6718b46a2/time_machine-2.19.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:f3589fee1ed0ab6ee424a55b0ea1ec694c4ba64cc26895bcd7d99f3d1bc6a28a", size = 20053, upload-time = "2025-08-19T17:21:17.704Z" }, { url = "https://files.pythonhosted.org/packages/45/e8/fe3376951e6118d8ec1d1f94066a169b791424fe4a26c7dfc069b153ee08/time_machine-2.19.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7887e85275c4975fe54df03dcdd5f38bd36be973adc68a8c77e17441c3b443d6", size = 15423, upload-time = "2025-08-19T17:21:18.668Z" }, { url = "https://files.pythonhosted.org/packages/9c/c7/f88d95cd1a87c650cf3749b4d64afdaf580297aa18ad7f4b44ec9d252dfc/time_machine-2.19.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ce0be294c209928563fcce1c587963e60ec803436cf1e181acd5bc1e425d554b", size = 39630, upload-time = "2025-08-19T17:21:19.645Z" }, { url = "https://files.pythonhosted.org/packages/cc/5d/65a5c48a65357e56ec6f032972e4abd1c02d4fca4b0717a3aaefd19014d4/time_machine-2.19.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a62fd1ab380012c86f4c042010418ed45eb31604f4bf4453e17c9fa60bc56a29", size = 41242, upload-time = "2025-08-19T17:21:20.979Z" }, { url = "https://files.pythonhosted.org/packages/f6/f9/fe5209e1615fde0a8cad6c4e857157b150333ed1fe31a7632b08cfe0ebdd/time_machine-2.19.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b25ec853a4530a5800731257f93206b12cbdee85ede964ebf8011b66086a7914", size = 44278, upload-time = "2025-08-19T17:21:21.984Z" }, { url = "https://files.pythonhosted.org/packages/4a/3a/a5e5fe9c5d614cde0a9387ff35e8dfd12c5ef6384e4c1a21b04e6e0b905d/time_machine-2.19.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a430e4d0e0556f021a9c78e9b9f68e5e8910bdace4aa34ed4d1a73e239ed9384", size = 42321, upload-time = "2025-08-19T17:21:23.755Z" }, { url = "https://files.pythonhosted.org/packages/a1/c5/56eca774e9162bc1ce59111d2bd69140dc8908c9478c92ec7bd15d547600/time_machine-2.19.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2415b7495ec4364c8067071e964fbadfe746dd4cdb43983f2f0bd6ebed13315c", size = 39270, upload-time = "2025-08-19T17:21:26.009Z" }, { url = "https://files.pythonhosted.org/packages/9b/69/5dd0c420667578169a12acc8c8fd7452e8cfb181e41c9b4ac7e88fa36686/time_machine-2.19.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dbfc6b90c10f288594e1bf89a728a98cc0030791fd73541bbdc6b090aff83143", size = 40193, upload-time = "2025-08-19T17:21:27.054Z" }, { url = "https://files.pythonhosted.org/packages/75/a7/de974d421bd55c9355583427c2a38fb0237bb5fd6614af492ba89dacb2f9/time_machine-2.19.0-cp313-cp313t-win32.whl", hash = "sha256:16f5d81f650c0a4d117ab08036dc30b5f8b262e11a4a0becc458e7f1c011b228", size = 17542, upload-time = "2025-08-19T17:21:28.674Z" }, { url = "https://files.pythonhosted.org/packages/76/0a/aa0d05becd5d06ae8d3f16d657dc8cc9400c8d79aef80299de196467ff12/time_machine-2.19.0-cp313-cp313t-win_amd64.whl", hash = "sha256:645699616ec14e147094f601e6ab9553ff6cea37fad9c42720a6d7ed04bcd5dc", size = 18703, upload-time = "2025-08-19T17:21:29.663Z" }, { url = "https://files.pythonhosted.org/packages/1f/c0/f785a4c7c73aa176510f7c48b84b49c26be84af0d534deb222e0327f750e/time_machine-2.19.0-cp313-cp313t-win_arm64.whl", hash = "sha256:b32daa965d13237536ea3afaa5ad61ade2b2d9314bc3a20196a0d2e1d7b57c6a", size = 17020, upload-time = "2025-08-19T17:21:30.653Z" }, { url = "https://files.pythonhosted.org/packages/ed/97/c5fb51def06c0b2b6735332ad118ab35b4d9b85368792e5b638e99b1b686/time_machine-2.19.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:31cb43c8fd2d961f31bed0ff4e0026964d2b35e5de9e0fabbfecf756906d3612", size = 19360, upload-time = "2025-08-19T17:21:31.94Z" }, { url = "https://files.pythonhosted.org/packages/2d/4e/2d795f7d6b7f5205ffe737a05bb1cf19d8038233b797062b2ef412b8512b/time_machine-2.19.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:bdf481a75afc6bff3e520db594501975b652f7def21cd1de6aa971d35ba644e6", size = 15033, upload-time = "2025-08-19T17:21:32.934Z" }, { url = "https://files.pythonhosted.org/packages/dd/32/9bad501e360b4e758c58fae616ca5f8c7ad974b343f2463a15b2bf77a366/time_machine-2.19.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:00bee4bb950ac6a08d62af78e4da0cf2b4fc2abf0de2320d0431bf610db06e7c", size = 33379, upload-time = "2025-08-19T17:21:33.925Z" }, { url = "https://files.pythonhosted.org/packages/a3/45/eda0ca4d793dfd162478d6163759b1c6ce7f6e61daa7fd7d62b31f21f87f/time_machine-2.19.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9f02199490906582302ce09edd32394fb393271674c75d7aa76c7a3245f16003", size = 35123, upload-time = "2025-08-19T17:21:34.945Z" }, { url = "https://files.pythonhosted.org/packages/f0/5a/97e16325442ae5731fcaac794f0a1ef9980eff8a5491e58201d7eb814a34/time_machine-2.19.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e35726c7ba625f844c13b1fc0d4f81f394eefaee1d3a094a9093251521f2ef15", size = 36588, upload-time = "2025-08-19T17:21:35.975Z" }, { url = "https://files.pythonhosted.org/packages/e8/9d/bf0b2ccc930cc4a316f26f1c78d3f313cd0fa13bb7480369b730a8f129db/time_machine-2.19.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:304315023999cd401ff02698870932b893369e1cfeb2248d09f6490507a92e97", size = 35013, upload-time = "2025-08-19T17:21:37.017Z" }, { url = "https://files.pythonhosted.org/packages/f0/5a/39ac6a3078174f9715d88364871348b249631f12e76de1b862433b3f8862/time_machine-2.19.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:9765d4f003f263ea8bfd90d2d15447ca4b3dfa181922cf6cf808923b02ac180a", size = 33303, upload-time = "2025-08-19T17:21:38.352Z" }, { url = "https://files.pythonhosted.org/packages/b3/ac/d8646baf9f95f2e792a6d7a7b35e92fca253c4a992afff801beafae0e5c2/time_machine-2.19.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7837ef3fd5911eb9b480909bb93d922737b6bdecea99dfcedb0a03807de9b2d3", size = 34440, upload-time = "2025-08-19T17:21:39.382Z" }, { url = "https://files.pythonhosted.org/packages/ce/8b/8b6568c5ae966d80ead03ab537be3c6acf2af06fb501c2d466a3162c6295/time_machine-2.19.0-cp314-cp314-win32.whl", hash = "sha256:4bb5bd43b1bdfac3007b920b51d8e761f024ed465cfeec63ac4296922a4ec428", size = 17162, upload-time = "2025-08-19T17:21:40.381Z" }, { url = "https://files.pythonhosted.org/packages/46/a5/211c1ab4566eba5308b2dc001b6349e3a032e3f6afa67ca2f27ea6b27af5/time_machine-2.19.0-cp314-cp314-win_amd64.whl", hash = "sha256:f583bbd0aa8ab4a7c45a684bf636d9e042d466e30bcbae1d13e7541e2cbe7207", size = 18040, upload-time = "2025-08-19T17:21:41.363Z" }, { url = "https://files.pythonhosted.org/packages/b8/fc/4c2fb705f6371cb83824da45a8b967514a922fc092a0ef53979334d97a70/time_machine-2.19.0-cp314-cp314-win_arm64.whl", hash = "sha256:f379c6f8a6575a8284592179cf528ce89373f060301323edcc44f1fa1d37be12", size = 16752, upload-time = "2025-08-19T17:21:42.336Z" }, { url = "https://files.pythonhosted.org/packages/79/ab/6437d18f31c666b5116c97572a282ac2590a82a0a9867746a6647eaf4613/time_machine-2.19.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:a3b8981f9c663b0906b05ab4d0ca211fae4b63b47c6ec26de5374fe56c836162", size = 20057, upload-time = "2025-08-19T17:21:43.35Z" }, { url = "https://files.pythonhosted.org/packages/6c/a2/e03639ec2ba7200328bbcad8a2b2b1d5fccca9cceb9481b164a1cabdcb33/time_machine-2.19.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8e9c6363893e7f52c226afbebb23e825259222d100e67dfd24c8a6d35f1a1907", size = 15430, upload-time = "2025-08-19T17:21:44.725Z" }, { url = "https://files.pythonhosted.org/packages/5d/ff/39e63a48e840f3e36ce24846ee51dd99c6dba635659b1750a2993771e88e/time_machine-2.19.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:206fcd6c9a6f00cac83db446ad1effc530a8cec244d2780af62db3a2d0a9871b", size = 39622, upload-time = "2025-08-19T17:21:45.821Z" }, { url = "https://files.pythonhosted.org/packages/9a/2e/ee5ac79c4954768705801e54817c7d58e07e25a0bb227e775f501f3e2122/time_machine-2.19.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bf33016a1403c123373ffaeff25e26e69d63bf2c63b6163932efed94160db7ef", size = 41235, upload-time = "2025-08-19T17:21:46.783Z" }, { url = "https://files.pythonhosted.org/packages/3a/3e/9af5f39525e779185c77285b8bbae15340eeeaa0afb33d458bc8b47d459b/time_machine-2.19.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9247c4bb9bbd3ff584ef4efbdec8efd9f37aa08bcfc4728bde1e489c2cb445bd", size = 44276, upload-time = "2025-08-19T17:21:47.759Z" }, { url = "https://files.pythonhosted.org/packages/59/fe/572c7443cc27140bbeae3947279bbd4a120f9e8622253a20637f260b7813/time_machine-2.19.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:77f9bb0b86758d1f2d9352642c874946ad5815df53ef4ca22eb9d532179fe50d", size = 42330, upload-time = "2025-08-19T17:21:48.881Z" }, { url = "https://files.pythonhosted.org/packages/cf/24/1a81c2e08ee7dae13ec8ceed27a29afa980c3d63852e42f1e023bf0faa03/time_machine-2.19.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0b529e262df3b9c449f427385f4d98250828c879168c2e00eec844439f40b370", size = 39281, upload-time = "2025-08-19T17:21:49.907Z" }, { url = "https://files.pythonhosted.org/packages/d2/60/6f0d6e5108978ca1a2a4ffb4d1c7e176d9199bb109fd44efe2680c60b52a/time_machine-2.19.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9199246e31cdc810e5d89cb71d09144c4d745960fdb0824da4994d152aca3303", size = 40201, upload-time = "2025-08-19T17:21:50.953Z" }, { url = "https://files.pythonhosted.org/packages/73/b9/3ea4951e8293b0643feb98c0b9a176fa822154f1810835db3f282968ab10/time_machine-2.19.0-cp314-cp314t-win32.whl", hash = "sha256:0fe81bae55b7aefc2c2a34eb552aa82e6c61a86b3353a3c70df79b9698cb02ca", size = 17743, upload-time = "2025-08-19T17:21:51.948Z" }, { url = "https://files.pythonhosted.org/packages/e4/8b/cd802884ca8a98e2b6cdc2397d57dd12ff8a7d1481e06fc3fad3d4e7e5ff/time_machine-2.19.0-cp314-cp314t-win_amd64.whl", hash = "sha256:7253791b8d7e7399fbeed7a8193cb01bc004242864306288797056badbdaf80b", size = 18956, upload-time = "2025-08-19T17:21:52.997Z" }, { url = "https://files.pythonhosted.org/packages/c6/49/cabb1593896082fd55e34768029b8b0ca23c9be8b2dc127e0fc14796d33e/time_machine-2.19.0-cp314-cp314t-win_arm64.whl", hash = "sha256:536bd1ac31ab06a1522e7bf287602188f502dc19d122b1502c4f60b1e8efac79", size = 17068, upload-time = "2025-08-19T17:21:54.064Z" }, { url = "https://files.pythonhosted.org/packages/d6/05/0608376c3167afe6cf7cdfd2b05c142ea4c42616eee9ba06d1799965806a/time_machine-2.19.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d8bb00b30ec9fe56d01e9812df1ffe39f331437cef9bfaebcc81c83f7f8f8ee2", size = 19659, upload-time = "2025-08-19T17:21:55.426Z" }, { url = "https://files.pythonhosted.org/packages/11/c4/72eb8c7b36830cf36c51d7bc2f1ac313d68881c3a58040fb6b42c4523d20/time_machine-2.19.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d821c60efc08a97cc11e5482798e6fd5eba5c0f22a02db246b50895dbdc0de41", size = 15153, upload-time = "2025-08-19T17:21:56.505Z" }, { url = "https://files.pythonhosted.org/packages/89/1a/0782e1f5c8ab8809ebd992709e1bb69d67600191baa023af7a5d32023a3c/time_machine-2.19.0-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:fb051aec7b3b6e96a200d911c225901e6133ff3da11e470e24111a53bbc13637", size = 32555, upload-time = "2025-08-19T17:21:57.74Z" }, { url = "https://files.pythonhosted.org/packages/94/b0/8ef58e2f6321851d5900ca3d18044938832c2ed42a2ac7570ca6aa29768a/time_machine-2.19.0-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fe59909d95a2ef5e01ce3354fdea3908404c2932c2069f00f66dff6f27e9363e", size = 34185, upload-time = "2025-08-19T17:21:59.361Z" }, { url = "https://files.pythonhosted.org/packages/82/74/ce0c9867f788c1fb22c417ec1aae47a24117e53d51f6ff97d7c6ca5392f6/time_machine-2.19.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:29e84b8682645b16eb6f9e8ec11c35324ad091841a11cf4fc3fc7f6119094c89", size = 35917, upload-time = "2025-08-19T17:22:00.421Z" }, { url = "https://files.pythonhosted.org/packages/d2/70/6f97a8f552dbaa66feb10170b5726dab74bc531673d1ed9d6f271547e54c/time_machine-2.19.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4a11f1c0e0d06023dc01614c964e256138913551d3ae6dca5148f79081156336", size = 34584, upload-time = "2025-08-19T17:22:01.447Z" }, { url = "https://files.pythonhosted.org/packages/48/c8/cf139088ce537c15d7f03cf56ec317d3a5cfb520e30aa711ea0248d0ae8a/time_machine-2.19.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:57a235a6307c54df50e69f1906e2f199e47da91bde4b886ee05aff57fe4b6bf6", size = 32608, upload-time = "2025-08-19T17:22:02.548Z" }, { url = "https://files.pythonhosted.org/packages/b1/17/0ec41ef7a30c6753fb226a28b74162b264b35724905ced4098f2f5076ded/time_machine-2.19.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:426aba552f7af9604adad9ef570c859af7c1081d878db78089fac159cd911b0a", size = 33686, upload-time = "2025-08-19T17:22:03.606Z" }, { url = "https://files.pythonhosted.org/packages/b0/19/586f15159083ec84f178d494c60758c46603b00c9641b04deb63f1950128/time_machine-2.19.0-cp39-cp39-win32.whl", hash = "sha256:67772c7197a3a712d1b970ed545c6e98db73524bd90e245fd3c8fa7ad7630768", size = 17133, upload-time = "2025-08-19T17:22:04.989Z" }, { url = "https://files.pythonhosted.org/packages/6a/c2/bfe4b906a9fe0bf2d011534314212ed752d6b8f392c9c82f6ac63dccc5ab/time_machine-2.19.0-cp39-cp39-win_amd64.whl", hash = "sha256:011d7859089263204dc5fdf83dce7388f986fe833c9381d6106b4edfda2ebd3e", size = 17972, upload-time = "2025-08-19T17:22:06.026Z" }, { url = "https://files.pythonhosted.org/packages/5d/73/182343eba05aa5787732aaa68f3b3feb5e40ddf86b928ae941be45646393/time_machine-2.19.0-cp39-cp39-win_arm64.whl", hash = "sha256:e1af66550fa4685434f00002808a525f176f1f92746646c0019bb86fbff48b27", size = 16820, upload-time = "2025-08-19T17:22:07.227Z" }, ] [[package]] name = "time-machine" version = "3.2.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.14' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and python_full_version < '3.14' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and python_full_version < '3.14' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra != 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", ] sdist = { url = "https://files.pythonhosted.org/packages/02/fc/37b02f6094dbb1f851145330460532176ed2f1dc70511a35828166c41e52/time_machine-3.2.0.tar.gz", hash = "sha256:a4ddd1cea17b8950e462d1805a42b20c81eb9aafc8f66b392dd5ce997e037d79", size = 14804, upload-time = "2025-12-17T23:33:02.599Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/9c/31/6bf41cb4a326230518d9b76c910dfc11d4fc23444d1cbfdf2d7652bd99f4/time_machine-3.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:68142c070e78b62215d8029ec7394905083a4f9aacb0a2a11514ce70b5951b13", size = 19447, upload-time = "2025-12-17T23:31:30.181Z" }, { url = "https://files.pythonhosted.org/packages/fa/14/d71ce771712e1cbfa15d8c24452225109262b16cb6caaf967e9f60662b67/time_machine-3.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:161bbd0648802ffdfcb4bb297ecb26b3009684a47d3a4dedb90bc549df4fa2ad", size = 15432, upload-time = "2025-12-17T23:31:31.381Z" }, { url = "https://files.pythonhosted.org/packages/8b/d6/dcb43a11f8029561996fad58ff9d3dc5e6d7f32b74f0745a2965d7e4b4f3/time_machine-3.2.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1359ba8c258be695ba69253bc84db882fd616fe69b426cc6056536da2c7bf68e", size = 32956, upload-time = "2025-12-17T23:31:32.469Z" }, { url = "https://files.pythonhosted.org/packages/77/da/d802cd3c335c414f9b11b479f7459aa72df5de6485c799966cfdf8856d53/time_machine-3.2.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c85b169998ca2c24a78fb214586ec11c4cad56d9c38f55ad8326235cb481c884", size = 34556, upload-time = "2025-12-17T23:31:33.946Z" }, { url = "https://files.pythonhosted.org/packages/85/ee/51ad553514ab0b940c7c82c6e1519dd10fd06ac07b32039a1d153ef09c88/time_machine-3.2.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:65b9367cb8a10505bc8f67da0da514ba20fa816fc47e11f434f7c60350322b4c", size = 36101, upload-time = "2025-12-17T23:31:35.462Z" }, { url = "https://files.pythonhosted.org/packages/11/39/938b111b5bb85a2b07502d0f9d8a704fc75bd760d62e76bce23c89ed16c9/time_machine-3.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9faca6a0f1973d7df3233c951fc2a11ff0c54df74087d8aaf41ae3deb19d0893", size = 34905, upload-time = "2025-12-17T23:31:36.543Z" }, { url = "https://files.pythonhosted.org/packages/dd/50/0951f73b23e76455de0b4a3a58ac5a24bd8d10489624b1c5e03f10c6fc0b/time_machine-3.2.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:213b1ada7f385d467e598999b642eda4a8e89ae10ad5dc4f5d8f672cbf604261", size = 33012, upload-time = "2025-12-17T23:31:37.967Z" }, { url = "https://files.pythonhosted.org/packages/4f/95/5304912d3dcecc4e14ed222dbe0396352efdf8497534abc3c9edd67a7528/time_machine-3.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:160b6afd94c39855af04d39c58e4cf602406abd6d79427ab80e830ea71789cfb", size = 34104, upload-time = "2025-12-17T23:31:39.449Z" }, { url = "https://files.pythonhosted.org/packages/d4/1c/af56518652ec7adac4ced193b7a42c4ff354fef28a412b3b5ffa5763aead/time_machine-3.2.0-cp310-cp310-win32.whl", hash = "sha256:c15d9ac257c78c124d112e4fc91fa9f3dcb004bdda913c19f0e7368d713cf080", size = 17468, upload-time = "2025-12-17T23:31:40.432Z" }, { url = "https://files.pythonhosted.org/packages/48/15/0213f00ca3cf6fe1c9fdbd7fd467e801052fc85534f30c0e4684bd474190/time_machine-3.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:3bf0f428487f93b8fe9d27aa01eccc817885da3290b467341b4a4a795e1d1891", size = 18313, upload-time = "2025-12-17T23:31:41.617Z" }, { url = "https://files.pythonhosted.org/packages/77/e4/811f96aa7a634b2b264d9a476f3400e710744dda503b4ad87a5c76db32c9/time_machine-3.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:347f6be2129fcd35b1c94b9387fcb2cbe7949b1e649228c5f22949a811b78976", size = 17037, upload-time = "2025-12-17T23:31:42.924Z" }, { url = "https://files.pythonhosted.org/packages/f5/e1/03aae5fbaa53859f665094af696338fc7cae733d926a024af69982712350/time_machine-3.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c188a9dda9fcf975022f1b325b466651b96a4dfc223c523ed7ed8d979f9bf3e8", size = 19143, upload-time = "2025-12-17T23:31:44.258Z" }, { url = "https://files.pythonhosted.org/packages/75/8f/98cb17bebb52b22ff4ec26984dd44280f9c71353c3bae0640a470e6683e5/time_machine-3.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17245f1cc2dd13f9d63a174be59bb2684a9e5e0a112ab707e37be92068cd655f", size = 15273, upload-time = "2025-12-17T23:31:45.246Z" }, { url = "https://files.pythonhosted.org/packages/dd/2f/ca11e4a7897234bb9331fcc5f4ed4714481ba4012370cc79a0ae8c42ea0a/time_machine-3.2.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d9bd1de1996e76efd36ae15970206c5089fb3728356794455bd5cd8d392b5537", size = 31049, upload-time = "2025-12-17T23:31:46.613Z" }, { url = "https://files.pythonhosted.org/packages/cf/ad/d17d83a59943094e6b6c6a3743caaf6811b12203c3e07a30cc7bcc2ab7ee/time_machine-3.2.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:98493cd50e8b7f941eab69b9e18e697ad69db1a0ec1959f78f3d7b0387107e5c", size = 32632, upload-time = "2025-12-17T23:31:47.72Z" }, { url = "https://files.pythonhosted.org/packages/71/50/d60576d047a0dfb5638cdfb335e9c3deb6e8528544fa0b3966a8480f72b7/time_machine-3.2.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:31f2a33d595d9f91eb9bc7f157f0dc5721f5789f4c4a9e8b852cdedb2a7d9b16", size = 34289, upload-time = "2025-12-17T23:31:48.913Z" }, { url = "https://files.pythonhosted.org/packages/fa/fe/4afa602dbdebddde6d0ea4a7fe849e49b9bb85dc3fb415725a87ccb4b471/time_machine-3.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9f78ac4213c10fbc44283edd1a29cfb7d3382484f4361783ddc057292aaa1889", size = 33175, upload-time = "2025-12-17T23:31:50.611Z" }, { url = "https://files.pythonhosted.org/packages/0d/87/c152e23977c1d7d7c94eb3ed3ea45cc55971796205125c6fdff40db2c60f/time_machine-3.2.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c1326b09e947b360926d529a96d1d9e126ce120359b63b506ecdc6ee20755c23", size = 31170, upload-time = "2025-12-17T23:31:51.645Z" }, { url = "https://files.pythonhosted.org/packages/80/af/54acf51d0f3ade3b51eab73df6192937c9a938753ef5456dff65eb8630be/time_machine-3.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9f2949f03d15264cc15c38918a2cda8966001f0f4ebe190cbfd9c56d91aed8ac", size = 32292, upload-time = "2025-12-17T23:31:52.803Z" }, { url = "https://files.pythonhosted.org/packages/cc/bc/3745963f36e75661a807196428639327a366f4332f35f1f775c074d4062f/time_machine-3.2.0-cp311-cp311-win32.whl", hash = "sha256:6dfe48e0499e6e16751476b9799e67be7514e6ef04cdf39571ef95a279645831", size = 17349, upload-time = "2025-12-17T23:31:54.19Z" }, { url = "https://files.pythonhosted.org/packages/82/a2/057469232a99d1f5a0160ae7c5bae7b095c9168b333dd598fcbcfbc1c87b/time_machine-3.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:809bdf267a29189c304154873620fe0bcc0c9513295fa46b19e21658231c4915", size = 18191, upload-time = "2025-12-17T23:31:55.472Z" }, { url = "https://files.pythonhosted.org/packages/79/d8/bf9c8de57262ee7130d92a6ed49ed6a6e40a36317e46979428d373630c12/time_machine-3.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:a3f4c17fa90f54902a3f8692c75caf67be87edc3429eeb71cb4595da58198f8e", size = 16905, upload-time = "2025-12-17T23:31:56.658Z" }, { url = "https://files.pythonhosted.org/packages/71/8b/080c8eedcd67921a52ba5bd0e075362062509ab63c86fc1a0442fad241a6/time_machine-3.2.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:cc4bee5b0214d7dc4ebc91f4a4c600f1a598e9b5606ac751f42cb6f6740b1dbb", size = 19255, upload-time = "2025-12-17T23:31:58.057Z" }, { url = "https://files.pythonhosted.org/packages/66/17/0e5291e9eb705bf8a5a1305f826e979af307bbeb79def4ddbf4b3f9a81e0/time_machine-3.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3ca036304b4460ae2fdc1b52dd8b1fa7cf1464daa427fc49567413c09aa839c1", size = 15360, upload-time = "2025-12-17T23:31:59.048Z" }, { url = "https://files.pythonhosted.org/packages/8b/e8/9ab87b71d2e2b62463b9b058b7ae7ac09fb57f8fcd88729dec169d304340/time_machine-3.2.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5442735b41d7a2abc2f04579b4ca6047ed4698a8338a4fec92c7c9423e7938cb", size = 33029, upload-time = "2025-12-17T23:32:00.413Z" }, { url = "https://files.pythonhosted.org/packages/4b/26/b5ca19da6f25ea905b3e10a0ea95d697c1aeba0404803a43c68f1af253e6/time_machine-3.2.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:97da3e971e505cb637079fb07ab0bcd36e33279f8ecac888ff131f45ef1e4d8d", size = 34579, upload-time = "2025-12-17T23:32:01.431Z" }, { url = "https://files.pythonhosted.org/packages/79/ca/6ac7ad5f10ea18cc1d9de49716ba38c32132c7b64532430d92ef240c116b/time_machine-3.2.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3cdda6dee4966e38aeb487309bb414c6cb23a81fc500291c77a8fcd3098832e7", size = 35961, upload-time = "2025-12-17T23:32:02.521Z" }, { url = "https://files.pythonhosted.org/packages/33/67/390dd958bed395ab32d79a9fe61fe111825c0dd4ded54dbba7e867f171e6/time_machine-3.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:33d9efd302a6998bcc8baa4d84f259f8a4081105bd3d7f7af7f1d0abd3b1c8aa", size = 34668, upload-time = "2025-12-17T23:32:03.585Z" }, { url = "https://files.pythonhosted.org/packages/da/57/c88fff034a4e9538b3ae7c68c9cfb283670b14d17522c5a8bc17d29f9a4b/time_machine-3.2.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3a0b0a33971f14145853c9bd95a6ab0353cf7e0019fa2a7aa1ae9fddfe8eab50", size = 32891, upload-time = "2025-12-17T23:32:04.656Z" }, { url = "https://files.pythonhosted.org/packages/2d/70/ebbb76022dba0fec8f9156540fc647e4beae1680c787c01b1b6200e56d70/time_machine-3.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2d0be9e5f22c38082d247a2cdcd8a936504e9db60b7b3606855fb39f299e9548", size = 34080, upload-time = "2025-12-17T23:32:06.146Z" }, { url = "https://files.pythonhosted.org/packages/db/9a/2ca9e7af3df540dc1c79e3de588adeddb7dcc2107829248e6969c4f14167/time_machine-3.2.0-cp312-cp312-win32.whl", hash = "sha256:3f74623648b936fdce5f911caf386c0a0b579456410975de8c0dfeaaffece1d8", size = 17371, upload-time = "2025-12-17T23:32:07.164Z" }, { url = "https://files.pythonhosted.org/packages/d8/ce/21d23efc9c2151939af1b7ee4e60d86d661b74ef32b8eaa148f6fe8c899c/time_machine-3.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:34e26a41d994b5e4b205136a90e9578470386749cc9a2ecf51ca18f83ce25e23", size = 18132, upload-time = "2025-12-17T23:32:08.447Z" }, { url = "https://files.pythonhosted.org/packages/2f/34/c2b70be483accf6db9e5d6c3139bce3c38fe51f898ccf64e8d3fe14fbf4d/time_machine-3.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:0615d3d82c418d6293f271c348945c5091a71f37e37173653d5c26d0e74b13a8", size = 16930, upload-time = "2025-12-17T23:32:09.477Z" }, { url = "https://files.pythonhosted.org/packages/ee/cd/43ad5efc88298af3c59b66769cea7f055567a85071579ed40536188530c1/time_machine-3.2.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c421a8eb85a4418a7675a41bf8660224318c46cc62e4751c8f1ceca752059090", size = 19318, upload-time = "2025-12-17T23:32:10.518Z" }, { url = "https://files.pythonhosted.org/packages/b0/f6/084010ef7f4a3f38b5a4900923d7c85b29e797655c4f6ee4ce54d903cca8/time_machine-3.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f4e758f7727d0058c4950c66b58200c187072122d6f7a98b610530a4233ea7b", size = 15390, upload-time = "2025-12-17T23:32:11.625Z" }, { url = "https://files.pythonhosted.org/packages/25/aa/1cabb74134f492270dc6860cb7865859bf40ecf828be65972827646e91ad/time_machine-3.2.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:154bd3f75c81f70218b2585cc12b60762fb2665c507eec5ec5037d8756d9b4e0", size = 33115, upload-time = "2025-12-17T23:32:13.219Z" }, { url = "https://files.pythonhosted.org/packages/5e/03/78c5d7dfa366924eb4dbfcc3fc917c39a4280ca234b12819cc1f16c03d88/time_machine-3.2.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d50cfe5ebea422c896ad8d278af9648412b7533b8ea6adeeee698a3fd9b1d3b7", size = 34705, upload-time = "2025-12-17T23:32:14.29Z" }, { url = "https://files.pythonhosted.org/packages/86/93/d5e877c24541f674c6869ff6e9c56833369796010190252e92c9d7ae5f0f/time_machine-3.2.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:636576501724bd6a9124e69d86e5aef263479e89ef739c5db361469f0463a0a1", size = 36104, upload-time = "2025-12-17T23:32:15.354Z" }, { url = "https://files.pythonhosted.org/packages/22/1c/d4bae72f388f67efc9609f89b012e434bb19d9549c7a7b47d6c7d9e5c55d/time_machine-3.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:40e6f40c57197fcf7ec32d2c563f4df0a82c42cdcc3cab27f688e98f6060df10", size = 34765, upload-time = "2025-12-17T23:32:16.434Z" }, { url = "https://files.pythonhosted.org/packages/1d/c3/ac378cf301d527d8dfad2f0db6bad0dfb1ab73212eaa56d6b96ee5d9d20b/time_machine-3.2.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a1bcf0b846bbfc19a79bc19e3fa04d8c7b1e8101c1b70340ffdb689cd801ea53", size = 33010, upload-time = "2025-12-17T23:32:17.532Z" }, { url = "https://files.pythonhosted.org/packages/06/35/7ce897319accda7a6970b288a9a8c52d25227342a7508505a2b3d235b649/time_machine-3.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ae55a56c179f4fe7a62575ad5148b6ed82f6c7e5cf2f9a9ec65f2f5b067db5f5", size = 34185, upload-time = "2025-12-17T23:32:18.566Z" }, { url = "https://files.pythonhosted.org/packages/bf/28/f922022269749cb02eee2b62919671153c4088994fa955a6b0e50327ff81/time_machine-3.2.0-cp313-cp313-win32.whl", hash = "sha256:a66fe55a107e46916007a391d4030479df8864ec6ad6f6a6528221befc5c886e", size = 17397, upload-time = "2025-12-17T23:32:19.605Z" }, { url = "https://files.pythonhosted.org/packages/ee/dc/fd87cde397f4a7bea493152f0aca8fd569ec709cad9e0f2ca7011eb8c7f7/time_machine-3.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:30c9ce57165df913e4f74e285a8ab829ff9b7aa3e5ec0973f88f642b9a7b3d15", size = 18139, upload-time = "2025-12-17T23:32:20.991Z" }, { url = "https://files.pythonhosted.org/packages/75/81/b8ce58233addc5d7d54d2fabc49dcbc02d79e3f079d150aa1bec3d5275ef/time_machine-3.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:89cad7e179e9bdcc84dcf09efe52af232c4cc7a01b3de868356bbd59d95bd9b8", size = 16964, upload-time = "2025-12-17T23:32:22.075Z" }, { url = "https://files.pythonhosted.org/packages/67/e7/487f0ba5fe6c58186a5e1af2a118dfa2c160fedb37ef53a7e972d410408e/time_machine-3.2.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:59d71545e62525a4b85b6de9ab5c02ee3c61110fd7f636139914a2335dcbfc9c", size = 20000, upload-time = "2025-12-17T23:32:23.058Z" }, { url = "https://files.pythonhosted.org/packages/e1/17/eb2c0054c8d44dd42df84ccd434539249a9c7d0b8eb53f799be2102500ab/time_machine-3.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:999672c621c35362bc28e03ca0c7df21500195540773c25993421fd8d6cc5003", size = 15657, upload-time = "2025-12-17T23:32:24.125Z" }, { url = "https://files.pythonhosted.org/packages/43/21/93443b5d1dd850f8bb9442e90d817a9033dcce6bfbdd3aabbb9786251c80/time_machine-3.2.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5faf7397f0580c7b9d67288522c8d7863e85f0cffadc0f1fccdb2c3dfce5783e", size = 39216, upload-time = "2025-12-17T23:32:25.542Z" }, { url = "https://files.pythonhosted.org/packages/9f/9e/18544cf8acc72bb1dc03762231c82ecc259733f4bb6770a7bbe5cd138603/time_machine-3.2.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d3dd886ec49f1fa5a00e844f5947e5c0f98ce574750c24b7424c6f77fc1c3e87", size = 40764, upload-time = "2025-12-17T23:32:26.643Z" }, { url = "https://files.pythonhosted.org/packages/27/f7/9fe9ce2795636a3a7467307af6bdf38bb613ddb701a8a5cd50ec713beb5e/time_machine-3.2.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:da0ecd96bc7bbe450acaaabe569d84e81688f1be8ad58d1470e42371d145fb53", size = 43526, upload-time = "2025-12-17T23:32:27.693Z" }, { url = "https://files.pythonhosted.org/packages/03/c1/a93e975ba9dec22e87ec92d18c28e67d36bd536f9119ffa439b2892b0c9c/time_machine-3.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:158220e946c1c4fb8265773a0282c88c35a7e3bb5d78e3561214e3b3231166f3", size = 41727, upload-time = "2025-12-17T23:32:28.985Z" }, { url = "https://files.pythonhosted.org/packages/5f/fb/e3633e5a6bbed1c76bb2e9810dabc2f8467532ffcd29b9aed404b473061a/time_machine-3.2.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c1aee29bc54356f248d5d7dfdd131e12ca825e850a08c0ebdb022266d073013", size = 38952, upload-time = "2025-12-17T23:32:30.031Z" }, { url = "https://files.pythonhosted.org/packages/82/3d/02e9fb2526b3d6b1b45bc8e4d912d95d1cd699d1a3f6df985817d37a0600/time_machine-3.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c8ed2224f09d25b1c2fc98683613aca12f90f682a427eabb68fc824d27014e4a", size = 39829, upload-time = "2025-12-17T23:32:31.075Z" }, { url = "https://files.pythonhosted.org/packages/85/c8/c14265212436da8e0814c45463987b3f57de3eca4de023cc2eabb0c62ef3/time_machine-3.2.0-cp313-cp313t-win32.whl", hash = "sha256:3498719f8dab51da76d29a20c1b5e52ee7db083dddf3056af7fa69c1b94e1fe6", size = 17852, upload-time = "2025-12-17T23:32:32.079Z" }, { url = "https://files.pythonhosted.org/packages/1d/bc/8acb13cf6149f47508097b158a9a8bec9ec4530a70cb406124e8023581f5/time_machine-3.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:e0d90bee170b219e1d15e6a58164aa808f5170090e4f090bd0670303e34181b1", size = 18918, upload-time = "2025-12-17T23:32:33.106Z" }, { url = "https://files.pythonhosted.org/packages/24/87/c443ee508c2708fd2514ccce9052f5e48888783ce690506919629ebc8eb0/time_machine-3.2.0-cp313-cp313t-win_arm64.whl", hash = "sha256:051de220fdb6e20d648111bbad423d9506fdbb2e44d4429cef3dc0382abf1fc2", size = 17261, upload-time = "2025-12-17T23:32:34.446Z" }, { url = "https://files.pythonhosted.org/packages/61/70/b4b980d126ed155c78d1879c50d60c8dcbd47bd11cb14ee7be50e0dfc07f/time_machine-3.2.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:1398980c017fe5744d66f419e0115ee48a53b00b146d738e1416c225eb610b82", size = 19303, upload-time = "2025-12-17T23:32:35.796Z" }, { url = "https://files.pythonhosted.org/packages/73/73/eaa33603c69a68fe2b6f54f9dd75481693d62f1d29676531002be06e2d1c/time_machine-3.2.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:4f8f4e35f4191ef70c2ab8ff490761ee9051b891afce2bf86dde3918eb7b537b", size = 15431, upload-time = "2025-12-17T23:32:37.244Z" }, { url = "https://files.pythonhosted.org/packages/76/10/b81e138e86cc7bab40cdb59d294b341e172201f4a6c84bb0ec080407977a/time_machine-3.2.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6db498686ecf6163c5aa8cf0bcd57bbe0f4081184f247edf3ee49a2612b584f9", size = 33206, upload-time = "2025-12-17T23:32:38.713Z" }, { url = "https://files.pythonhosted.org/packages/d3/72/4deab446b579e8bd5dca91de98595c5d6bd6a17ce162abf5c5f2ce40d3d8/time_machine-3.2.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:027c1807efb74d0cd58ad16524dec94212fbe900115d70b0123399883657ac0f", size = 34792, upload-time = "2025-12-17T23:32:40.223Z" }, { url = "https://files.pythonhosted.org/packages/2c/39/439c6b587ddee76d533fe972289d0646e0a5520e14dc83d0a30aeb5565f7/time_machine-3.2.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92432610c05676edd5e6946a073c6f0c926923123ce7caee1018dc10782c713d", size = 36187, upload-time = "2025-12-17T23:32:41.705Z" }, { url = "https://files.pythonhosted.org/packages/4b/db/2da4368db15180989bab83746a857bde05ad16e78f326801c142bb747a06/time_machine-3.2.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c25586b62480eb77ef3d953fba273209478e1ef49654592cd6a52a68dfe56a67", size = 34855, upload-time = "2025-12-17T23:32:42.817Z" }, { url = "https://files.pythonhosted.org/packages/88/84/120a431fee50bc4c241425bee4d3a4910df4923b7ab5f7dff1bf0c772f08/time_machine-3.2.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:6bf3a2fa738d15e0b95d14469a0b8ea42635467408d8b490e263d5d45c9a177f", size = 33222, upload-time = "2025-12-17T23:32:43.94Z" }, { url = "https://files.pythonhosted.org/packages/f9/ea/89cfda82bb8c57ff91bb9a26751aa234d6d90e9b4d5ab0ad9dce0f9f0329/time_machine-3.2.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ce76b82276d7ad2a66cdc85dad4df19d1422b69183170a34e8fbc4c3f35502f7", size = 34270, upload-time = "2025-12-17T23:32:45.037Z" }, { url = "https://files.pythonhosted.org/packages/8a/aa/235357da4f69a51a8d35fcbfcfa77cdc7dc24f62ae54025006570bda7e2d/time_machine-3.2.0-cp314-cp314-win32.whl", hash = "sha256:14d6778273c543441863dff712cd1d7803dee946b18de35921eb8df10714539d", size = 17544, upload-time = "2025-12-17T23:32:46.099Z" }, { url = "https://files.pythonhosted.org/packages/7b/51/6c8405a7276be79693b792cff22ce41067ec05db26a7d02f2d5b06324434/time_machine-3.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:cbf821da96dbc80d349fa9e7c36e670b41d68a878d28c8850057992fed430eef", size = 18423, upload-time = "2025-12-17T23:32:47.468Z" }, { url = "https://files.pythonhosted.org/packages/d9/03/a3cf419e20c35fc203c6e4fed48b5b667c1a2b4da456d9971e605f73ecef/time_machine-3.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:71c75d71f8e68abc8b669bca26ed2ddd558430a6c171e32b8620288565f18c0e", size = 17050, upload-time = "2025-12-17T23:32:48.91Z" }, { url = "https://files.pythonhosted.org/packages/86/a1/142de946dc4393f910bf4564b5c3ba819906e1f49b06c9cb557519c849e4/time_machine-3.2.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:4e374779021446fc2b5c29d80457ec9a3b1a5df043dc2aae07d7c1415d52323c", size = 19991, upload-time = "2025-12-17T23:32:49.933Z" }, { url = "https://files.pythonhosted.org/packages/ee/62/7f17def6289901f94726921811a16b9adce46e666362c75d45730c60274f/time_machine-3.2.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:122310a6af9c36e9a636da32830e591e7923e8a07bdd0a43276c3a36c6821c90", size = 15707, upload-time = "2025-12-17T23:32:50.969Z" }, { url = "https://files.pythonhosted.org/packages/5d/d3/3502fb9bd3acb159c18844b26c43220201a0d4a622c0c853785d07699a92/time_machine-3.2.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ba3eeb0f018cc362dd8128befa3426696a2e16dd223c3fb695fde184892d4d8c", size = 39207, upload-time = "2025-12-17T23:32:52.033Z" }, { url = "https://files.pythonhosted.org/packages/5a/be/8b27f4aa296fda14a5a2ad7f588ddd450603c33415ab3f8e85b2f1a44678/time_machine-3.2.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:77d38ba664b381a7793f8786efc13b5004f0d5f672dae814430445b8202a67a6", size = 40764, upload-time = "2025-12-17T23:32:53.167Z" }, { url = "https://files.pythonhosted.org/packages/42/cd/fe4c4e5c8ab6d48fab3624c32be9116fb120173a35fe67e482e5cf68b3d2/time_machine-3.2.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f09abeb8f03f044d72712207e0489a62098ad3ad16dac38927fcf80baca4d6a7", size = 43508, upload-time = "2025-12-17T23:32:54.597Z" }, { url = "https://files.pythonhosted.org/packages/b4/28/5a3ba2fce85b97655a425d6bb20a441550acd2b304c96b2c19d3839f721a/time_machine-3.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6b28367ce4f73987a55e230e1d30a57a3af85da8eb1a140074eb6e8c7e6ef19f", size = 41712, upload-time = "2025-12-17T23:32:55.781Z" }, { url = "https://files.pythonhosted.org/packages/81/58/e38084be7fdabb4835db68a3a47e58c34182d79fc35df1ecbe0db2c5359f/time_machine-3.2.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:903c7751c904581da9f7861c3015bed7cdc40047321291d3694a3cdc783bbca3", size = 38939, upload-time = "2025-12-17T23:32:56.867Z" }, { url = "https://files.pythonhosted.org/packages/40/d0/ad3feb0a392ef4e0c08bc32024950373ddc0669002cbdcbb9f3bf0c2d114/time_machine-3.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:528217cad85ede5f85c8bc78b0341868d3c3cfefc6ecb5b622e1cacb6c73247b", size = 39837, upload-time = "2025-12-17T23:32:58.283Z" }, { url = "https://files.pythonhosted.org/packages/5b/9e/5f4b2ea63b267bd78f3245e76f5528836611b5f2d30b5e7300a722fe4428/time_machine-3.2.0-cp314-cp314t-win32.whl", hash = "sha256:75724762ffd517e7e80aaec1fad1ff5a7414bd84e2b3ee7a0bacfeb67c14926e", size = 18091, upload-time = "2025-12-17T23:32:59.403Z" }, { url = "https://files.pythonhosted.org/packages/39/6f/456b1f4d2700ae02b19eba830f870596a4b89b74bac3b6c80666f1b108c5/time_machine-3.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2526abbd053c5bca898d1b3e7898eec34626b12206718d8c7ce88fd12c1c9c5c", size = 19208, upload-time = "2025-12-17T23:33:00.488Z" }, { url = "https://files.pythonhosted.org/packages/2f/22/8063101427ecd3d2652aada4d21d0876b07a3dc789125bca2ee858fec3ed/time_machine-3.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:7f2fb6784b414edbe2c0b558bfaab0c251955ba27edd62946cce4a01675a992c", size = 17359, upload-time = "2025-12-17T23:33:01.54Z" }, ] [[package]] name = "tomli" version = "2.4.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/82/30/31573e9457673ab10aa432461bee537ce6cef177667deca369efb79df071/tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c", size = 17477, upload-time = "2026-01-11T11:22:38.165Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/3c/d9/3dc2289e1f3b32eb19b9785b6a006b28ee99acb37d1d47f78d4c10e28bf8/tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867", size = 153663, upload-time = "2026-01-11T11:21:45.27Z" }, { url = "https://files.pythonhosted.org/packages/51/32/ef9f6845e6b9ca392cd3f64f9ec185cc6f09f0a2df3db08cbe8809d1d435/tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9", size = 148469, upload-time = "2026-01-11T11:21:46.873Z" }, { url = "https://files.pythonhosted.org/packages/d6/c2/506e44cce89a8b1b1e047d64bd495c22c9f71f21e05f380f1a950dd9c217/tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95", size = 236039, upload-time = "2026-01-11T11:21:48.503Z" }, { url = "https://files.pythonhosted.org/packages/b3/40/e1b65986dbc861b7e986e8ec394598187fa8aee85b1650b01dd925ca0be8/tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76", size = 243007, upload-time = "2026-01-11T11:21:49.456Z" }, { url = "https://files.pythonhosted.org/packages/9c/6f/6e39ce66b58a5b7ae572a0f4352ff40c71e8573633deda43f6a379d56b3e/tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d", size = 240875, upload-time = "2026-01-11T11:21:50.755Z" }, { url = "https://files.pythonhosted.org/packages/aa/ad/cb089cb190487caa80204d503c7fd0f4d443f90b95cf4ef5cf5aa0f439b0/tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576", size = 246271, upload-time = "2026-01-11T11:21:51.81Z" }, { url = "https://files.pythonhosted.org/packages/0b/63/69125220e47fd7a3a27fd0de0c6398c89432fec41bc739823bcc66506af6/tomli-2.4.0-cp311-cp311-win32.whl", hash = "sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a", size = 96770, upload-time = "2026-01-11T11:21:52.647Z" }, { url = "https://files.pythonhosted.org/packages/1e/0d/a22bb6c83f83386b0008425a6cd1fa1c14b5f3dd4bad05e98cf3dbbf4a64/tomli-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa", size = 107626, upload-time = "2026-01-11T11:21:53.459Z" }, { url = "https://files.pythonhosted.org/packages/2f/6d/77be674a3485e75cacbf2ddba2b146911477bd887dda9d8c9dfb2f15e871/tomli-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614", size = 94842, upload-time = "2026-01-11T11:21:54.831Z" }, { url = "https://files.pythonhosted.org/packages/3c/43/7389a1869f2f26dba52404e1ef13b4784b6b37dac93bac53457e3ff24ca3/tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1", size = 154894, upload-time = "2026-01-11T11:21:56.07Z" }, { url = "https://files.pythonhosted.org/packages/e9/05/2f9bf110b5294132b2edf13fe6ca6ae456204f3d749f623307cbb7a946f2/tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8", size = 149053, upload-time = "2026-01-11T11:21:57.467Z" }, { url = "https://files.pythonhosted.org/packages/e8/41/1eda3ca1abc6f6154a8db4d714a4d35c4ad90adc0bcf700657291593fbf3/tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a", size = 243481, upload-time = "2026-01-11T11:21:58.661Z" }, { url = "https://files.pythonhosted.org/packages/d2/6d/02ff5ab6c8868b41e7d4b987ce2b5f6a51d3335a70aa144edd999e055a01/tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1", size = 251720, upload-time = "2026-01-11T11:22:00.178Z" }, { url = "https://files.pythonhosted.org/packages/7b/57/0405c59a909c45d5b6f146107c6d997825aa87568b042042f7a9c0afed34/tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b", size = 247014, upload-time = "2026-01-11T11:22:01.238Z" }, { url = "https://files.pythonhosted.org/packages/2c/0e/2e37568edd944b4165735687cbaf2fe3648129e440c26d02223672ee0630/tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51", size = 251820, upload-time = "2026-01-11T11:22:02.727Z" }, { url = "https://files.pythonhosted.org/packages/5a/1c/ee3b707fdac82aeeb92d1a113f803cf6d0f37bdca0849cb489553e1f417a/tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729", size = 97712, upload-time = "2026-01-11T11:22:03.777Z" }, { url = "https://files.pythonhosted.org/packages/69/13/c07a9177d0b3bab7913299b9278845fc6eaaca14a02667c6be0b0a2270c8/tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da", size = 108296, upload-time = "2026-01-11T11:22:04.86Z" }, { url = "https://files.pythonhosted.org/packages/18/27/e267a60bbeeee343bcc279bb9e8fbed0cbe224bc7b2a3dc2975f22809a09/tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3", size = 94553, upload-time = "2026-01-11T11:22:05.854Z" }, { url = "https://files.pythonhosted.org/packages/34/91/7f65f9809f2936e1f4ce6268ae1903074563603b2a2bd969ebbda802744f/tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0", size = 154915, upload-time = "2026-01-11T11:22:06.703Z" }, { url = "https://files.pythonhosted.org/packages/20/aa/64dd73a5a849c2e8f216b755599c511badde80e91e9bc2271baa7b2cdbb1/tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e", size = 149038, upload-time = "2026-01-11T11:22:07.56Z" }, { url = "https://files.pythonhosted.org/packages/9e/8a/6d38870bd3d52c8d1505ce054469a73f73a0fe62c0eaf5dddf61447e32fa/tomli-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4", size = 242245, upload-time = "2026-01-11T11:22:08.344Z" }, { url = "https://files.pythonhosted.org/packages/59/bb/8002fadefb64ab2669e5b977df3f5e444febea60e717e755b38bb7c41029/tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e", size = 250335, upload-time = "2026-01-11T11:22:09.951Z" }, { url = "https://files.pythonhosted.org/packages/a5/3d/4cdb6f791682b2ea916af2de96121b3cb1284d7c203d97d92d6003e91c8d/tomli-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c", size = 245962, upload-time = "2026-01-11T11:22:11.27Z" }, { url = "https://files.pythonhosted.org/packages/f2/4a/5f25789f9a460bd858ba9756ff52d0830d825b458e13f754952dd15fb7bb/tomli-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f", size = 250396, upload-time = "2026-01-11T11:22:12.325Z" }, { url = "https://files.pythonhosted.org/packages/aa/2f/b73a36fea58dfa08e8b3a268750e6853a6aac2a349241a905ebd86f3047a/tomli-2.4.0-cp313-cp313-win32.whl", hash = "sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86", size = 97530, upload-time = "2026-01-11T11:22:13.865Z" }, { url = "https://files.pythonhosted.org/packages/3b/af/ca18c134b5d75de7e8dc551c5234eaba2e8e951f6b30139599b53de9c187/tomli-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87", size = 108227, upload-time = "2026-01-11T11:22:15.224Z" }, { url = "https://files.pythonhosted.org/packages/22/c3/b386b832f209fee8073c8138ec50f27b4460db2fdae9ffe022df89a57f9b/tomli-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132", size = 94748, upload-time = "2026-01-11T11:22:16.009Z" }, { url = "https://files.pythonhosted.org/packages/f3/c4/84047a97eb1004418bc10bdbcfebda209fca6338002eba2dc27cc6d13563/tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6", size = 154725, upload-time = "2026-01-11T11:22:17.269Z" }, { url = "https://files.pythonhosted.org/packages/a8/5d/d39038e646060b9d76274078cddf146ced86dc2b9e8bbf737ad5983609a0/tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc", size = 148901, upload-time = "2026-01-11T11:22:18.287Z" }, { url = "https://files.pythonhosted.org/packages/73/e5/383be1724cb30f4ce44983d249645684a48c435e1cd4f8b5cded8a816d3c/tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66", size = 243375, upload-time = "2026-01-11T11:22:19.154Z" }, { url = "https://files.pythonhosted.org/packages/31/f0/bea80c17971c8d16d3cc109dc3585b0f2ce1036b5f4a8a183789023574f2/tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d", size = 250639, upload-time = "2026-01-11T11:22:20.168Z" }, { url = "https://files.pythonhosted.org/packages/2c/8f/2853c36abbb7608e3f945d8a74e32ed3a74ee3a1f468f1ffc7d1cb3abba6/tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702", size = 246897, upload-time = "2026-01-11T11:22:21.544Z" }, { url = "https://files.pythonhosted.org/packages/49/f0/6c05e3196ed5337b9fe7ea003e95fd3819a840b7a0f2bf5a408ef1dad8ed/tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8", size = 254697, upload-time = "2026-01-11T11:22:23.058Z" }, { url = "https://files.pythonhosted.org/packages/f3/f5/2922ef29c9f2951883525def7429967fc4d8208494e5ab524234f06b688b/tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776", size = 98567, upload-time = "2026-01-11T11:22:24.033Z" }, { url = "https://files.pythonhosted.org/packages/7b/31/22b52e2e06dd2a5fdbc3ee73226d763b184ff21fc24e20316a44ccc4d96b/tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475", size = 108556, upload-time = "2026-01-11T11:22:25.378Z" }, { url = "https://files.pythonhosted.org/packages/48/3d/5058dff3255a3d01b705413f64f4306a141a8fd7a251e5a495e3f192a998/tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2", size = 96014, upload-time = "2026-01-11T11:22:26.138Z" }, { url = "https://files.pythonhosted.org/packages/b8/4e/75dab8586e268424202d3a1997ef6014919c941b50642a1682df43204c22/tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9", size = 163339, upload-time = "2026-01-11T11:22:27.143Z" }, { url = "https://files.pythonhosted.org/packages/06/e3/b904d9ab1016829a776d97f163f183a48be6a4deb87304d1e0116a349519/tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0", size = 159490, upload-time = "2026-01-11T11:22:28.399Z" }, { url = "https://files.pythonhosted.org/packages/e3/5a/fc3622c8b1ad823e8ea98a35e3c632ee316d48f66f80f9708ceb4f2a0322/tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df", size = 269398, upload-time = "2026-01-11T11:22:29.345Z" }, { url = "https://files.pythonhosted.org/packages/fd/33/62bd6152c8bdd4c305ad9faca48f51d3acb2df1f8791b1477d46ff86e7f8/tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d", size = 276515, upload-time = "2026-01-11T11:22:30.327Z" }, { url = "https://files.pythonhosted.org/packages/4b/ff/ae53619499f5235ee4211e62a8d7982ba9e439a0fb4f2f351a93d67c1dd2/tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f", size = 273806, upload-time = "2026-01-11T11:22:32.56Z" }, { url = "https://files.pythonhosted.org/packages/47/71/cbca7787fa68d4d0a9f7072821980b39fbb1b6faeb5f5cf02f4a5559fa28/tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b", size = 281340, upload-time = "2026-01-11T11:22:33.505Z" }, { url = "https://files.pythonhosted.org/packages/f5/00/d595c120963ad42474cf6ee7771ad0d0e8a49d0f01e29576ee9195d9ecdf/tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087", size = 108106, upload-time = "2026-01-11T11:22:34.451Z" }, { url = "https://files.pythonhosted.org/packages/de/69/9aa0c6a505c2f80e519b43764f8b4ba93b5a0bbd2d9a9de6e2b24271b9a5/tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd", size = 120504, upload-time = "2026-01-11T11:22:35.764Z" }, { url = "https://files.pythonhosted.org/packages/b3/9f/f1668c281c58cfae01482f7114a4b88d345e4c140386241a1a24dcc9e7bc/tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4", size = 99561, upload-time = "2026-01-11T11:22:36.624Z" }, { url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477, upload-time = "2026-01-11T11:22:37.446Z" }, ] [[package]] name = "types-awscrt" version = "0.31.3" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/76/26/0aa563e229c269c528a3b8c709fc671ac2a5c564732fab0852ac6ee006cf/types_awscrt-0.31.3.tar.gz", hash = "sha256:09d3eaf00231e0f47e101bd9867e430873bc57040050e2a3bd8305cb4fc30865", size = 18178, upload-time = "2026-03-08T02:31:14.569Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/3e/e5/47a573bbbd0a790f8f9fe452f7188ea72b212d21c9be57d5fc0cbc442075/types_awscrt-0.31.3-py3-none-any.whl", hash = "sha256:e5ce65a00a2ab4f35eacc1e3d700d792338d56e4823ee7b4dbe017f94cfc4458", size = 43340, upload-time = "2026-03-08T02:31:13.38Z" }, ] [[package]] name = "types-deprecated" version = "1.3.1.20260130" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.10'", ] sdist = { url = "https://files.pythonhosted.org/packages/b5/97/9924e496f88412788c432891cacd041e542425fe0bffff4143a7c1c89ac4/types_deprecated-1.3.1.20260130.tar.gz", hash = "sha256:726b05e5e66d42359b1d6631835b15de62702588c8a59b877aa4b1e138453450", size = 8455, upload-time = "2026-01-30T03:58:17.401Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/d2/b2/6f920582af7efcd37165cd6321707f3ad5839dd24565a8a982f2bd9c6fd1/types_deprecated-1.3.1.20260130-py3-none-any.whl", hash = "sha256:593934d85c38ca321a9d301f00c42ffe13e4cf830b71b10579185ba0ce172d9a", size = 9077, upload-time = "2026-01-30T03:58:16.633Z" }, ] [[package]] name = "types-deprecated" version = "1.3.1.20260408" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.14' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and python_full_version < '3.14' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and python_full_version < '3.14' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra != 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", ] sdist = { url = "https://files.pythonhosted.org/packages/1a/db/076de3e81b106d3cec17aec9640ab1b2d02f29bad441de280459c161ce65/types_deprecated-1.3.1.20260408.tar.gz", hash = "sha256:62d6a86d0cc754c14bb2de31162d069b1c6a07ce11ee65e5258f8f75308eb3a3", size = 8524, upload-time = "2026-04-08T04:26:39.894Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/53/d0/d3258379deb749d949c3c72313981c9d2cceec518b87dcf506f022f5d49f/types_deprecated-1.3.1.20260408-py3-none-any.whl", hash = "sha256:b64e1eab560d4fa9394a27a3099211344b0e0f2f3ac8026d825c86e70d65cdd5", size = 9079, upload-time = "2026-04-08T04:26:38.752Z" }, ] [[package]] name = "types-python-dateutil" version = "2.9.0.20260124" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.10'", ] sdist = { url = "https://files.pythonhosted.org/packages/fe/41/4f8eb1ce08688a9e3e23709ed07089ccdeaf95b93745bfb768c6da71197d/types_python_dateutil-2.9.0.20260124.tar.gz", hash = "sha256:7d2db9f860820c30e5b8152bfe78dbdf795f7d1c6176057424e8b3fdd1f581af", size = 16596, upload-time = "2026-01-24T03:18:42.975Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/5a/c2/aa5e3f4103cc8b1dcf92432415dde75d70021d634ecfd95b2e913cf43e17/types_python_dateutil-2.9.0.20260124-py3-none-any.whl", hash = "sha256:f802977ae08bf2260142e7ca1ab9d4403772a254409f7bbdf652229997124951", size = 18266, upload-time = "2026-01-24T03:18:42.155Z" }, ] [[package]] name = "types-python-dateutil" version = "2.9.0.20260408" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.14' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and python_full_version < '3.14' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and python_full_version < '3.14' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra != 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", ] sdist = { url = "https://files.pythonhosted.org/packages/88/f3/2427775f80cd5e19a0a71ba8e5ab7645a01a852f43a5fd0ffc24f66338e0/types_python_dateutil-2.9.0.20260408.tar.gz", hash = "sha256:8b056ec01568674235f64ecbcef928972a5fac412f5aab09c516dfa2acfbb582", size = 16981, upload-time = "2026-04-08T04:28:10.995Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/fd/c6/eeba37bfee282a6a97f889faef9352d6172c6a5088eb9a4daf570d9d748d/types_python_dateutil-2.9.0.20260408-py3-none-any.whl", hash = "sha256:473139d514a71c9d1fbd8bb328974bedcb1cc3dba57aad04ffa4157f483c216f", size = 18437, upload-time = "2026-04-08T04:28:10.095Z" }, ] [[package]] name = "types-s3transfer" version = "0.16.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/fe/64/42689150509eb3e6e82b33ee3d89045de1592488842ddf23c56957786d05/types_s3transfer-0.16.0.tar.gz", hash = "sha256:b4636472024c5e2b62278c5b759661efeb52a81851cde5f092f24100b1ecb443", size = 13557, upload-time = "2025-12-08T08:13:09.928Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/98/27/e88220fe6274eccd3bdf95d9382918716d312f6f6cef6a46332d1ee2feff/types_s3transfer-0.16.0-py3-none-any.whl", hash = "sha256:1c0cd111ecf6e21437cb410f5cddb631bfb2263b77ad973e79b9c6d0cb24e0ef", size = 19247, upload-time = "2025-12-08T08:13:08.426Z" }, ] [[package]] name = "typing-extensions" version = "4.15.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" } 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" }, ] [[package]] name = "typing-inspection" version = "0.4.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions", marker = "extra == 'extra-9-anthropic-mcp' or extra != 'group-9-anthropic-pydantic-v1' or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, ] [[package]] name = "urllib3" version = "1.26.20" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.10'", ] sdist = { url = "https://files.pythonhosted.org/packages/e4/e8/6ff5e6bc22095cfc59b6ea711b687e2b7ed4bdb373f7eeec370a97d7392f/urllib3-1.26.20.tar.gz", hash = "sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32", size = 307380, upload-time = "2024-08-29T15:43:11.37Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/33/cf/8435d5a7159e2a9c83a95896ed596f68cf798005fe107cc655b5c5c14704/urllib3-1.26.20-py2.py3-none-any.whl", hash = "sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e", size = 144225, upload-time = "2024-08-29T15:43:08.921Z" }, ] [[package]] name = "urllib3" version = "2.6.3" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.14' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and python_full_version < '3.14' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and python_full_version < '3.14' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra != 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", ] sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, ] [[package]] name = "uvicorn" version = "0.42.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click", marker = "python_full_version >= '3.10'" }, { name = "h11", marker = "python_full_version >= '3.10'" }, { name = "typing-extensions", marker = "python_full_version == '3.10.*'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e3/ad/4a96c425be6fb67e0621e62d86c402b4a17ab2be7f7c055d9bd2f638b9e2/uvicorn-0.42.0.tar.gz", hash = "sha256:9b1f190ce15a2dd22e7758651d9b6d12df09a13d51ba5bf4fc33c383a48e1775", size = 85393, upload-time = "2026-03-16T06:19:50.077Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/0a/89/f8827ccff89c1586027a105e5630ff6139a64da2515e24dafe860bd9ae4d/uvicorn-0.42.0-py3-none-any.whl", hash = "sha256:96c30f5c7abe6f74ae8900a70e92b85ad6613b745d4879eb9b16ccad15645359", size = 68830, upload-time = "2026-03-16T06:19:48.325Z" }, ] [[package]] name = "wrapt" version = "2.1.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/2e/64/925f213fdcbb9baeb1530449ac71a4d57fc361c053d06bf78d0c5c7cd80c/wrapt-2.1.2.tar.gz", hash = "sha256:3996a67eecc2c68fd47b4e3c564405a5777367adfd9b8abb58387b63ee83b21e", size = 81678, upload-time = "2026-03-06T02:53:25.134Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/da/d2/387594fb592d027366645f3d7cc9b4d7ca7be93845fbaba6d835a912ef3c/wrapt-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b7a86d99a14f76facb269dc148590c01aaf47584071809a70da30555228158c", size = 60669, upload-time = "2026-03-06T02:52:40.671Z" }, { url = "https://files.pythonhosted.org/packages/c9/18/3f373935bc5509e7ac444c8026a56762e50c1183e7061797437ca96c12ce/wrapt-2.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a819e39017f95bf7aede768f75915635aa8f671f2993c036991b8d3bfe8dbb6f", size = 61603, upload-time = "2026-03-06T02:54:21.032Z" }, { url = "https://files.pythonhosted.org/packages/c2/7a/32758ca2853b07a887a4574b74e28843919103194bb47001a304e24af62f/wrapt-2.1.2-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5681123e60aed0e64c7d44f72bbf8b4ce45f79d81467e2c4c728629f5baf06eb", size = 113632, upload-time = "2026-03-06T02:53:54.121Z" }, { url = "https://files.pythonhosted.org/packages/1d/d5/eeaa38f670d462e97d978b3b0d9ce06d5b91e54bebac6fbed867809216e7/wrapt-2.1.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b8b28e97a44d21836259739ae76284e180b18abbb4dcfdff07a415cf1016c3e", size = 115644, upload-time = "2026-03-06T02:54:53.33Z" }, { url = "https://files.pythonhosted.org/packages/e3/09/2a41506cb17affb0bdf9d5e2129c8c19e192b388c4c01d05e1b14db23c00/wrapt-2.1.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cef91c95a50596fcdc31397eb6955476f82ae8a3f5a8eabdc13611b60ee380ba", size = 112016, upload-time = "2026-03-06T02:54:43.274Z" }, { url = "https://files.pythonhosted.org/packages/64/15/0e6c3f5e87caadc43db279724ee36979246d5194fa32fed489c73643ba59/wrapt-2.1.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dad63212b168de8569b1c512f4eac4b57f2c6934b30df32d6ee9534a79f1493f", size = 114823, upload-time = "2026-03-06T02:54:29.392Z" }, { url = "https://files.pythonhosted.org/packages/56/b2/0ad17c8248f4e57bedf44938c26ec3ee194715f812d2dbbd9d7ff4be6c06/wrapt-2.1.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d307aa6888d5efab2c1cde09843d48c843990be13069003184b67d426d145394", size = 111244, upload-time = "2026-03-06T02:54:02.149Z" }, { url = "https://files.pythonhosted.org/packages/ff/04/bcdba98c26f2c6522c7c09a726d5d9229120163493620205b2f76bd13c01/wrapt-2.1.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c87cf3f0c85e27b3ac7d9ad95da166bf8739ca215a8b171e8404a2d739897a45", size = 113307, upload-time = "2026-03-06T02:54:12.428Z" }, { url = "https://files.pythonhosted.org/packages/0e/1b/5e2883c6bc14143924e465a6fc5a92d09eeabe35310842a481fb0581f832/wrapt-2.1.2-cp310-cp310-win32.whl", hash = "sha256:d1c5fea4f9fe3762e2b905fdd67df51e4be7a73b7674957af2d2ade71a5c075d", size = 57986, upload-time = "2026-03-06T02:54:26.823Z" }, { url = "https://files.pythonhosted.org/packages/42/5a/4efc997bccadd3af5749c250b49412793bc41e13a83a486b2b54a33e240c/wrapt-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:d8f7740e1af13dff2684e4d56fe604a7e04d6c94e737a60568d8d4238b9a0c71", size = 60336, upload-time = "2026-03-06T02:54:18Z" }, { url = "https://files.pythonhosted.org/packages/c1/f5/a2bb833e20181b937e87c242645ed5d5aa9c373006b0467bfe1a35c727d0/wrapt-2.1.2-cp310-cp310-win_arm64.whl", hash = "sha256:1c6cc827c00dc839350155f316f1f8b4b0c370f52b6a19e782e2bda89600c7dc", size = 58757, upload-time = "2026-03-06T02:53:51.545Z" }, { url = "https://files.pythonhosted.org/packages/c7/81/60c4471fce95afa5922ca09b88a25f03c93343f759aae0f31fb4412a85c7/wrapt-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:96159a0ee2b0277d44201c3b5be479a9979cf154e8c82fa5df49586a8e7679bb", size = 60666, upload-time = "2026-03-06T02:52:58.934Z" }, { url = "https://files.pythonhosted.org/packages/6b/be/80e80e39e7cb90b006a0eaf11c73ac3a62bbfb3068469aec15cc0bc795de/wrapt-2.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:98ba61833a77b747901e9012072f038795de7fc77849f1faa965464f3f87ff2d", size = 61601, upload-time = "2026-03-06T02:53:00.487Z" }, { url = "https://files.pythonhosted.org/packages/b0/be/d7c88cd9293c859fc74b232abdc65a229bb953997995d6912fc85af18323/wrapt-2.1.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:767c0dbbe76cae2a60dd2b235ac0c87c9cccf4898aef8062e57bead46b5f6894", size = 114057, upload-time = "2026-03-06T02:52:44.08Z" }, { url = "https://files.pythonhosted.org/packages/ea/25/36c04602831a4d685d45a93b3abea61eca7fe35dab6c842d6f5d570ef94a/wrapt-2.1.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c691a6bc752c0cc4711cc0c00896fcd0f116abc253609ef64ef930032821842", size = 116099, upload-time = "2026-03-06T02:54:56.74Z" }, { url = "https://files.pythonhosted.org/packages/5c/4e/98a6eb417ef551dc277bec1253d5246b25003cf36fdf3913b65cb7657a56/wrapt-2.1.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f3b7d73012ea75aee5844de58c88f44cf62d0d62711e39da5a82824a7c4626a8", size = 112457, upload-time = "2026-03-06T02:53:52.842Z" }, { url = "https://files.pythonhosted.org/packages/cb/a6/a6f7186a5297cad8ec53fd7578533b28f795fdf5372368c74bd7e6e9841c/wrapt-2.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:577dff354e7acd9d411eaf4bfe76b724c89c89c8fc9b7e127ee28c5f7bcb25b6", size = 115351, upload-time = "2026-03-06T02:53:32.684Z" }, { url = "https://files.pythonhosted.org/packages/97/6f/06e66189e721dbebd5cf20e138acc4d1150288ce118462f2fcbff92d38db/wrapt-2.1.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3d7b6fd105f8b24e5bd23ccf41cb1d1099796524bcc6f7fbb8fe576c44befbc9", size = 111748, upload-time = "2026-03-06T02:53:08.455Z" }, { url = "https://files.pythonhosted.org/packages/ef/43/4808b86f499a51370fbdbdfa6cb91e9b9169e762716456471b619fca7a70/wrapt-2.1.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:866abdbf4612e0b34764922ef8b1c5668867610a718d3053d59e24a5e5fcfc15", size = 113783, upload-time = "2026-03-06T02:53:02.02Z" }, { url = "https://files.pythonhosted.org/packages/91/2c/a3f28b8fa7ac2cefa01cfcaca3471f9b0460608d012b693998cd61ef43df/wrapt-2.1.2-cp311-cp311-win32.whl", hash = "sha256:5a0a0a3a882393095573344075189eb2d566e0fd205a2b6414e9997b1b800a8b", size = 57977, upload-time = "2026-03-06T02:53:27.844Z" }, { url = "https://files.pythonhosted.org/packages/3f/c3/2b1c7bd07a27b1db885a2fab469b707bdd35bddf30a113b4917a7e2139d2/wrapt-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:64a07a71d2730ba56f11d1a4b91f7817dc79bc134c11516b75d1921a7c6fcda1", size = 60336, upload-time = "2026-03-06T02:54:28.104Z" }, { url = "https://files.pythonhosted.org/packages/ec/5c/76ece7b401b088daa6503d6264dd80f9a727df3e6042802de9a223084ea2/wrapt-2.1.2-cp311-cp311-win_arm64.whl", hash = "sha256:b89f095fe98bc12107f82a9f7d570dc83a0870291aeb6b1d7a7d35575f55d98a", size = 58756, upload-time = "2026-03-06T02:53:16.319Z" }, { url = "https://files.pythonhosted.org/packages/4c/b6/1db817582c49c7fcbb7df6809d0f515af29d7c2fbf57eb44c36e98fb1492/wrapt-2.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ff2aad9c4cda28a8f0653fc2d487596458c2a3f475e56ba02909e950a9efa6a9", size = 61255, upload-time = "2026-03-06T02:52:45.663Z" }, { url = "https://files.pythonhosted.org/packages/a2/16/9b02a6b99c09227c93cd4b73acc3678114154ec38da53043c0ddc1fba0dc/wrapt-2.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6433ea84e1cfacf32021d2a4ee909554ade7fd392caa6f7c13f1f4bf7b8e8748", size = 61848, upload-time = "2026-03-06T02:53:48.728Z" }, { url = "https://files.pythonhosted.org/packages/af/aa/ead46a88f9ec3a432a4832dfedb84092fc35af2d0ba40cd04aea3889f247/wrapt-2.1.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c20b757c268d30d6215916a5fa8461048d023865d888e437fab451139cad6c8e", size = 121433, upload-time = "2026-03-06T02:54:40.328Z" }, { url = "https://files.pythonhosted.org/packages/3a/9f/742c7c7cdf58b59085a1ee4b6c37b013f66ac33673a7ef4aaed5e992bc33/wrapt-2.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79847b83eb38e70d93dc392c7c5b587efe65b3e7afcc167aa8abd5d60e8761c8", size = 123013, upload-time = "2026-03-06T02:53:26.58Z" }, { url = "https://files.pythonhosted.org/packages/e8/44/2c3dd45d53236b7ed7c646fcf212251dc19e48e599debd3926b52310fafb/wrapt-2.1.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f8fba1bae256186a83d1875b2b1f4e2d1242e8fac0f58ec0d7e41b26967b965c", size = 117326, upload-time = "2026-03-06T02:53:11.547Z" }, { url = "https://files.pythonhosted.org/packages/74/e2/b17d66abc26bd96f89dec0ecd0ef03da4a1286e6ff793839ec431b9fae57/wrapt-2.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e3d3b35eedcf5f7d022291ecd7533321c4775f7b9cd0050a31a68499ba45757c", size = 121444, upload-time = "2026-03-06T02:54:09.5Z" }, { url = "https://files.pythonhosted.org/packages/3c/62/e2977843fdf9f03daf1586a0ff49060b1b2fc7ff85a7ea82b6217c1ae36e/wrapt-2.1.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:6f2c5390460de57fa9582bc8a1b7a6c86e1a41dfad74c5225fc07044c15cc8d1", size = 116237, upload-time = "2026-03-06T02:54:03.884Z" }, { url = "https://files.pythonhosted.org/packages/88/dd/27fc67914e68d740bce512f11734aec08696e6b17641fef8867c00c949fc/wrapt-2.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7dfa9f2cf65d027b951d05c662cc99ee3bd01f6e4691ed39848a7a5fffc902b2", size = 120563, upload-time = "2026-03-06T02:53:20.412Z" }, { url = "https://files.pythonhosted.org/packages/ec/9f/b750b3692ed2ef4705cb305bd68858e73010492b80e43d2a4faa5573cbe7/wrapt-2.1.2-cp312-cp312-win32.whl", hash = "sha256:eba8155747eb2cae4a0b913d9ebd12a1db4d860fc4c829d7578c7b989bd3f2f0", size = 58198, upload-time = "2026-03-06T02:53:37.732Z" }, { url = "https://files.pythonhosted.org/packages/8e/b2/feecfe29f28483d888d76a48f03c4c4d8afea944dbee2b0cd3380f9df032/wrapt-2.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:1c51c738d7d9faa0b3601708e7e2eda9bf779e1b601dce6c77411f2a1b324a63", size = 60441, upload-time = "2026-03-06T02:52:47.138Z" }, { url = "https://files.pythonhosted.org/packages/44/e1/e328f605d6e208547ea9fd120804fcdec68536ac748987a68c47c606eea8/wrapt-2.1.2-cp312-cp312-win_arm64.whl", hash = "sha256:c8e46ae8e4032792eb2f677dbd0d557170a8e5524d22acc55199f43efedd39bf", size = 58836, upload-time = "2026-03-06T02:53:22.053Z" }, { url = "https://files.pythonhosted.org/packages/4c/7a/d936840735c828b38d26a854e85d5338894cda544cb7a85a9d5b8b9c4df7/wrapt-2.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787fd6f4d67befa6fe2abdffcbd3de2d82dfc6fb8a6d850407c53332709d030b", size = 61259, upload-time = "2026-03-06T02:53:41.922Z" }, { url = "https://files.pythonhosted.org/packages/5e/88/9a9b9a90ac8ca11c2fdb6a286cb3a1fc7dd774c00ed70929a6434f6bc634/wrapt-2.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4bdf26e03e6d0da3f0e9422fd36bcebf7bc0eeb55fdf9c727a09abc6b9fe472e", size = 61851, upload-time = "2026-03-06T02:52:48.672Z" }, { url = "https://files.pythonhosted.org/packages/03/a9/5b7d6a16fd6533fed2756900fc8fc923f678179aea62ada6d65c92718c00/wrapt-2.1.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bbac24d879aa22998e87f6b3f481a5216311e7d53c7db87f189a7a0266dafffb", size = 121446, upload-time = "2026-03-06T02:54:14.013Z" }, { url = "https://files.pythonhosted.org/packages/45/bb/34c443690c847835cfe9f892be78c533d4f32366ad2888972c094a897e39/wrapt-2.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:16997dfb9d67addc2e3f41b62a104341e80cac52f91110dece393923c0ebd5ca", size = 123056, upload-time = "2026-03-06T02:54:10.829Z" }, { url = "https://files.pythonhosted.org/packages/93/b9/ff205f391cb708f67f41ea148545f2b53ff543a7ac293b30d178af4d2271/wrapt-2.1.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:162e4e2ba7542da9027821cb6e7c5e068d64f9a10b5f15512ea28e954893a267", size = 117359, upload-time = "2026-03-06T02:53:03.623Z" }, { url = "https://files.pythonhosted.org/packages/1f/3d/1ea04d7747825119c3c9a5e0874a40b33594ada92e5649347c457d982805/wrapt-2.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f29c827a8d9936ac320746747a016c4bc66ef639f5cd0d32df24f5eacbf9c69f", size = 121479, upload-time = "2026-03-06T02:53:45.844Z" }, { url = "https://files.pythonhosted.org/packages/78/cc/ee3a011920c7a023b25e8df26f306b2484a531ab84ca5c96260a73de76c0/wrapt-2.1.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:a9dd9813825f7ecb018c17fd147a01845eb330254dff86d3b5816f20f4d6aaf8", size = 116271, upload-time = "2026-03-06T02:54:46.356Z" }, { url = "https://files.pythonhosted.org/packages/98/fd/e5ff7ded41b76d802cf1191288473e850d24ba2e39a6ec540f21ae3b57cb/wrapt-2.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6f8dbdd3719e534860d6a78526aafc220e0241f981367018c2875178cf83a413", size = 120573, upload-time = "2026-03-06T02:52:50.163Z" }, { url = "https://files.pythonhosted.org/packages/47/c5/242cae3b5b080cd09bacef0591691ba1879739050cc7c801ff35c8886b66/wrapt-2.1.2-cp313-cp313-win32.whl", hash = "sha256:5c35b5d82b16a3bc6e0a04349b606a0582bc29f573786aebe98e0c159bc48db6", size = 58205, upload-time = "2026-03-06T02:53:47.494Z" }, { url = "https://files.pythonhosted.org/packages/12/69/c358c61e7a50f290958809b3c61ebe8b3838ea3e070d7aac9814f95a0528/wrapt-2.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:f8bc1c264d8d1cf5b3560a87bbdd31131573eb25f9f9447bb6252b8d4c44a3a1", size = 60452, upload-time = "2026-03-06T02:53:30.038Z" }, { url = "https://files.pythonhosted.org/packages/8e/66/c8a6fcfe321295fd8c0ab1bd685b5a01462a9b3aa2f597254462fc2bc975/wrapt-2.1.2-cp313-cp313-win_arm64.whl", hash = "sha256:3beb22f674550d5634642c645aba4c72a2c66fb185ae1aebe1e955fae5a13baf", size = 58842, upload-time = "2026-03-06T02:52:52.114Z" }, { url = "https://files.pythonhosted.org/packages/da/55/9c7052c349106e0b3f17ae8db4b23a691a963c334de7f9dbd60f8f74a831/wrapt-2.1.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0fc04bc8664a8bc4c8e00b37b5355cffca2535209fba1abb09ae2b7c76ddf82b", size = 63075, upload-time = "2026-03-06T02:53:19.108Z" }, { url = "https://files.pythonhosted.org/packages/09/a8/ce7b4006f7218248dd71b7b2b732d0710845a0e49213b18faef64811ffef/wrapt-2.1.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a9b9d50c9af998875a1482a038eb05755dfd6fe303a313f6a940bb53a83c3f18", size = 63719, upload-time = "2026-03-06T02:54:33.452Z" }, { url = "https://files.pythonhosted.org/packages/e4/e5/2ca472e80b9e2b7a17f106bb8f9df1db11e62101652ce210f66935c6af67/wrapt-2.1.2-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2d3ff4f0024dd224290c0eabf0240f1bfc1f26363431505fb1b0283d3b08f11d", size = 152643, upload-time = "2026-03-06T02:52:42.721Z" }, { url = "https://files.pythonhosted.org/packages/36/42/30f0f2cefca9d9cbf6835f544d825064570203c3e70aa873d8ae12e23791/wrapt-2.1.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3278c471f4468ad544a691b31bb856374fbdefb7fee1a152153e64019379f015", size = 158805, upload-time = "2026-03-06T02:54:25.441Z" }, { url = "https://files.pythonhosted.org/packages/bb/67/d08672f801f604889dcf58f1a0b424fe3808860ede9e03affc1876b295af/wrapt-2.1.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a8914c754d3134a3032601c6984db1c576e6abaf3fc68094bb8ab1379d75ff92", size = 145990, upload-time = "2026-03-06T02:53:57.456Z" }, { url = "https://files.pythonhosted.org/packages/68/a7/fd371b02e73babec1de6ade596e8cd9691051058cfdadbfd62a5898f3295/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ff95d4264e55839be37bafe1536db2ab2de19da6b65f9244f01f332b5286cfbf", size = 155670, upload-time = "2026-03-06T02:54:55.309Z" }, { url = "https://files.pythonhosted.org/packages/86/2d/9fe0095dfdb621009f40117dcebf41d7396c2c22dca6eac779f4c007b86c/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:76405518ca4e1b76fbb1b9f686cff93aebae03920cc55ceeec48ff9f719c5f67", size = 144357, upload-time = "2026-03-06T02:54:24.092Z" }, { url = "https://files.pythonhosted.org/packages/0e/b6/ec7b4a254abbe4cde9fa15c5d2cca4518f6b07d0f1b77d4ee9655e30280e/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c0be8b5a74c5824e9359b53e7e58bef71a729bacc82e16587db1c4ebc91f7c5a", size = 150269, upload-time = "2026-03-06T02:53:31.268Z" }, { url = "https://files.pythonhosted.org/packages/6e/6b/2fabe8ebf148f4ee3c782aae86a795cc68ffe7d432ef550f234025ce0cfa/wrapt-2.1.2-cp313-cp313t-win32.whl", hash = "sha256:f01277d9a5fc1862f26f7626da9cf443bebc0abd2f303f41c5e995b15887dabd", size = 59894, upload-time = "2026-03-06T02:54:15.391Z" }, { url = "https://files.pythonhosted.org/packages/ca/fb/9ba66fc2dedc936de5f8073c0217b5d4484e966d87723415cc8262c5d9c2/wrapt-2.1.2-cp313-cp313t-win_amd64.whl", hash = "sha256:84ce8f1c2104d2f6daa912b1b5b039f331febfeee74f8042ad4e04992bd95c8f", size = 63197, upload-time = "2026-03-06T02:54:41.943Z" }, { url = "https://files.pythonhosted.org/packages/c0/1c/012d7423c95d0e337117723eb8ecf73c622ce15a97847e84cf3f8f26cd7e/wrapt-2.1.2-cp313-cp313t-win_arm64.whl", hash = "sha256:a93cd767e37faeddbe07d8fc4212d5cba660af59bdb0f6372c93faaa13e6e679", size = 60363, upload-time = "2026-03-06T02:54:48.093Z" }, { url = "https://files.pythonhosted.org/packages/39/25/e7ea0b417db02bb796182a5316398a75792cd9a22528783d868755e1f669/wrapt-2.1.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1370e516598854e5b4366e09ce81e08bfe94d42b0fd569b88ec46cc56d9164a9", size = 61418, upload-time = "2026-03-06T02:53:55.706Z" }, { url = "https://files.pythonhosted.org/packages/ec/0f/fa539e2f6a770249907757eaeb9a5ff4deb41c026f8466c1c6d799088a9b/wrapt-2.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6de1a3851c27e0bd6a04ca993ea6f80fc53e6c742ee1601f486c08e9f9b900a9", size = 61914, upload-time = "2026-03-06T02:52:53.37Z" }, { url = "https://files.pythonhosted.org/packages/53/37/02af1867f5b1441aaeda9c82deed061b7cd1372572ddcd717f6df90b5e93/wrapt-2.1.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:de9f1a2bbc5ac7f6012ec24525bdd444765a2ff64b5985ac6e0692144838542e", size = 120417, upload-time = "2026-03-06T02:54:30.74Z" }, { url = "https://files.pythonhosted.org/packages/c3/b7/0138a6238c8ba7476c77cf786a807f871672b37f37a422970342308276e7/wrapt-2.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:970d57ed83fa040d8b20c52fe74a6ae7e3775ae8cff5efd6a81e06b19078484c", size = 122797, upload-time = "2026-03-06T02:54:51.539Z" }, { url = "https://files.pythonhosted.org/packages/e1/ad/819ae558036d6a15b7ed290d5b14e209ca795dd4da9c58e50c067d5927b0/wrapt-2.1.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3969c56e4563c375861c8df14fa55146e81ac11c8db49ea6fb7f2ba58bc1ff9a", size = 117350, upload-time = "2026-03-06T02:54:37.651Z" }, { url = "https://files.pythonhosted.org/packages/8b/2d/afc18dc57a4600a6e594f77a9ae09db54f55ba455440a54886694a84c71b/wrapt-2.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:57d7c0c980abdc5f1d98b11a2aa3bb159790add80258c717fa49a99921456d90", size = 121223, upload-time = "2026-03-06T02:54:35.221Z" }, { url = "https://files.pythonhosted.org/packages/b9/5b/5ec189b22205697bc56eb3b62aed87a1e0423e9c8285d0781c7a83170d15/wrapt-2.1.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:776867878e83130c7a04237010463372e877c1c994d449ca6aaafeab6aab2586", size = 116287, upload-time = "2026-03-06T02:54:19.654Z" }, { url = "https://files.pythonhosted.org/packages/f7/2d/f84939a7c9b5e6cdd8a8d0f6a26cabf36a0f7e468b967720e8b0cd2bdf69/wrapt-2.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:fab036efe5464ec3291411fabb80a7a39e2dd80bae9bcbeeca5087fdfa891e19", size = 119593, upload-time = "2026-03-06T02:54:16.697Z" }, { url = "https://files.pythonhosted.org/packages/0b/fe/ccd22a1263159c4ac811ab9374c061bcb4a702773f6e06e38de5f81a1bdc/wrapt-2.1.2-cp314-cp314-win32.whl", hash = "sha256:e6ed62c82ddf58d001096ae84ce7f833db97ae2263bff31c9b336ba8cfe3f508", size = 58631, upload-time = "2026-03-06T02:53:06.498Z" }, { url = "https://files.pythonhosted.org/packages/65/0a/6bd83be7bff2e7efaac7b4ac9748da9d75a34634bbbbc8ad077d527146df/wrapt-2.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:467e7c76315390331c67073073d00662015bb730c566820c9ca9b54e4d67fd04", size = 60875, upload-time = "2026-03-06T02:53:50.252Z" }, { url = "https://files.pythonhosted.org/packages/6c/c0/0b3056397fe02ff80e5a5d72d627c11eb885d1ca78e71b1a5c1e8c7d45de/wrapt-2.1.2-cp314-cp314-win_arm64.whl", hash = "sha256:da1f00a557c66225d53b095a97eace0fc5349e3bfda28fa34ffae238978ee575", size = 59164, upload-time = "2026-03-06T02:53:59.128Z" }, { url = "https://files.pythonhosted.org/packages/71/ed/5d89c798741993b2371396eb9d4634f009ff1ad8a6c78d366fe2883ea7a6/wrapt-2.1.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:62503ffbc2d3a69891cf29beeaccdb4d5e0a126e2b6a851688d4777e01428dbb", size = 63163, upload-time = "2026-03-06T02:52:54.873Z" }, { url = "https://files.pythonhosted.org/packages/c6/8c/05d277d182bf36b0a13d6bd393ed1dec3468a25b59d01fba2dd70fe4d6ae/wrapt-2.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c7e6cd120ef837d5b6f860a6ea3745f8763805c418bb2f12eeb1fa6e25f22d22", size = 63723, upload-time = "2026-03-06T02:52:56.374Z" }, { url = "https://files.pythonhosted.org/packages/f4/27/6c51ec1eff4413c57e72d6106bb8dec6f0c7cdba6503d78f0fa98767bcc9/wrapt-2.1.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3769a77df8e756d65fbc050333f423c01ae012b4f6731aaf70cf2bef61b34596", size = 152652, upload-time = "2026-03-06T02:53:23.79Z" }, { url = "https://files.pythonhosted.org/packages/db/4c/d7dd662d6963fc7335bfe29d512b02b71cdfa23eeca7ab3ac74a67505deb/wrapt-2.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a76d61a2e851996150ba0f80582dd92a870643fa481f3b3846f229de88caf044", size = 158807, upload-time = "2026-03-06T02:53:35.742Z" }, { url = "https://files.pythonhosted.org/packages/b4/4d/1e5eea1a78d539d346765727422976676615814029522c76b87a95f6bcdd/wrapt-2.1.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6f97edc9842cf215312b75fe737ee7c8adda75a89979f8e11558dfff6343cc4b", size = 146061, upload-time = "2026-03-06T02:52:57.574Z" }, { url = "https://files.pythonhosted.org/packages/89/bc/62cabea7695cd12a288023251eeefdcb8465056ddaab6227cb78a2de005b/wrapt-2.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4006c351de6d5007aa33a551f600404ba44228a89e833d2fadc5caa5de8edfbf", size = 155667, upload-time = "2026-03-06T02:53:39.422Z" }, { url = "https://files.pythonhosted.org/packages/e9/99/6f2888cd68588f24df3a76572c69c2de28287acb9e1972bf0c83ce97dbc1/wrapt-2.1.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a9372fc3639a878c8e7d87e1556fa209091b0a66e912c611e3f833e2c4202be2", size = 144392, upload-time = "2026-03-06T02:54:22.41Z" }, { url = "https://files.pythonhosted.org/packages/40/51/1dfc783a6c57971614c48e361a82ca3b6da9055879952587bc99fe1a7171/wrapt-2.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3144b027ff30cbd2fca07c0a87e67011adb717eb5f5bd8496325c17e454257a3", size = 150296, upload-time = "2026-03-06T02:54:07.848Z" }, { url = "https://files.pythonhosted.org/packages/6c/38/cbb8b933a0201076c1f64fc42883b0023002bdc14a4964219154e6ff3350/wrapt-2.1.2-cp314-cp314t-win32.whl", hash = "sha256:3b8d15e52e195813efe5db8cec156eebe339aaf84222f4f4f051a6c01f237ed7", size = 60539, upload-time = "2026-03-06T02:54:00.594Z" }, { url = "https://files.pythonhosted.org/packages/82/dd/e5176e4b241c9f528402cebb238a36785a628179d7d8b71091154b3e4c9e/wrapt-2.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:08ffa54146a7559f5b8df4b289b46d963a8e74ed16ba3687f99896101a3990c5", size = 63969, upload-time = "2026-03-06T02:54:39Z" }, { url = "https://files.pythonhosted.org/packages/5c/99/79f17046cf67e4a95b9987ea129632ba8bcec0bc81f3fb3d19bdb0bd60cd/wrapt-2.1.2-cp314-cp314t-win_arm64.whl", hash = "sha256:72aaa9d0d8e4ed0e2e98019cea47a21f823c9dd4b43c7b77bba6679ffcca6a00", size = 60554, upload-time = "2026-03-06T02:53:14.132Z" }, { url = "https://files.pythonhosted.org/packages/f7/ea/fe375f8a012e5f25b2cd31b093860c8c6540be445345c6f886e5d8bca9ef/wrapt-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5e0fa9cc32300daf9eb09a1f5bdc6deb9a79defd70d5356ba453bcd50aef3742", size = 60661, upload-time = "2026-03-06T02:54:06.572Z" }, { url = "https://files.pythonhosted.org/packages/d8/2a/0dff969ddf4d3f69f051c8f81afbd3a9fc9fb08ab993b1061ee582b6543c/wrapt-2.1.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:710f6e5dfaf6a5d5c397d2d6758a78fecd9649deb21f1b645f5b57a328d63050", size = 61602, upload-time = "2026-03-06T02:53:44.48Z" }, { url = "https://files.pythonhosted.org/packages/25/62/b80dd7a6c21486a7b8aea63b6bac509b2e4ea184b0eefe3795aa7202a92c/wrapt-2.1.2-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:305d8a1755116bfdad5dda9e771dcb2138990a1d66e9edd81658816edf51aed1", size = 113340, upload-time = "2026-03-06T02:54:44.626Z" }, { url = "https://files.pythonhosted.org/packages/82/06/adbe093e07a775d8687cc45329cda9e1b33779357d146c688accbc3a9f1f/wrapt-2.1.2-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f0d8fc30a43b5fe191cf2b1a0c82bab2571dadd38e7c0062ee87d6df858dd06e", size = 115305, upload-time = "2026-03-06T02:53:04.929Z" }, { url = "https://files.pythonhosted.org/packages/3f/dd/31c2596c6bf6bfb1874aa637c66e3028baa83d00708d1439db3b395f8371/wrapt-2.1.2-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a5d516e22aedb7c9c1d47cba1c63160b1a6f61ec2f3948d127cd38d5cfbb556f", size = 111691, upload-time = "2026-03-06T02:53:17.845Z" }, { url = "https://files.pythonhosted.org/packages/03/92/e9ba179f4a00b7eb7ab8afc1f729fc3be8bd468b9f1d33be1fd99476493a/wrapt-2.1.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:45914e8efbe4b9d5102fcf0e8e2e3258b83a5d5fba9f8f7b6d15681e9d29ffe0", size = 114507, upload-time = "2026-03-06T02:54:49.398Z" }, { url = "https://files.pythonhosted.org/packages/0f/dd/5ce1332e824503fb7041a8f8b51ec1f06e7033834e38c01416fa1c599668/wrapt-2.1.2-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:478282ebd3795a089154fb16d3db360e103aa13d3b2ad30f8f6aac0d2207de0e", size = 110945, upload-time = "2026-03-06T02:54:32.088Z" }, { url = "https://files.pythonhosted.org/packages/1b/17/d1c1d7b63a029205fe8add19db654fd105e2a92a3776c1312e74456ce3ab/wrapt-2.1.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3756219045f73fb28c5d7662778e4156fbd06cf823c4d2d4b19f97305e52819c", size = 113107, upload-time = "2026-03-06T02:54:05.226Z" }, { url = "https://files.pythonhosted.org/packages/85/9f/aa5b1570ca36a0533ad5fc9d9e436047b9af187f9bd182f5eb6b718fe28b/wrapt-2.1.2-cp39-cp39-win32.whl", hash = "sha256:b8aefb4dbb18d904b96827435a763fa42fc1f08ea096a391710407a60983ced8", size = 57984, upload-time = "2026-03-06T02:53:10.07Z" }, { url = "https://files.pythonhosted.org/packages/71/3a/a0c92e4c8b6cd8ef179c62249f03f5ce50c142f71fe04c2a14279bd826b4/wrapt-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:e5aeab8fe15c3dff75cfee94260dcd9cded012d4ff06add036c28fae7718593b", size = 60334, upload-time = "2026-03-06T02:53:34.183Z" }, { url = "https://files.pythonhosted.org/packages/75/87/2725632aa7f1f70a9730952444e2ba856bd15ce8ee0210afcdb50f48ab69/wrapt-2.1.2-cp39-cp39-win_arm64.whl", hash = "sha256:f069e113743a21a3defac6677f000068ebb931639f789b5b226598e247a4c89e", size = 58759, upload-time = "2026-03-06T02:53:43.16Z" }, { url = "https://files.pythonhosted.org/packages/1a/c7/8528ac2dfa2c1e6708f647df7ae144ead13f0a31146f43c7264b4942bf12/wrapt-2.1.2-py3-none-any.whl", hash = "sha256:b8fd6fa2b2c4e7621808f8c62e8317f4aae56e59721ad933bac5239d913cf0e8", size = 43993, upload-time = "2026-03-06T02:53:12.905Z" }, ] [[package]] name = "yarl" version = "1.22.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version < '3.10'", ] dependencies = [ { name = "idna", marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "multidict", marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "propcache", marker = "python_full_version < '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169, upload-time = "2025-10-06T14:12:55.963Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/d1/43/a2204825342f37c337f5edb6637040fa14e365b2fcc2346960201d457579/yarl-1.22.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c7bd6683587567e5a49ee6e336e0612bec8329be1b7d4c8af5687dcdeb67ee1e", size = 140517, upload-time = "2025-10-06T14:08:42.494Z" }, { url = "https://files.pythonhosted.org/packages/44/6f/674f3e6f02266428c56f704cd2501c22f78e8b2eeb23f153117cc86fb28a/yarl-1.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5cdac20da754f3a723cceea5b3448e1a2074866406adeb4ef35b469d089adb8f", size = 93495, upload-time = "2025-10-06T14:08:46.2Z" }, { url = "https://files.pythonhosted.org/packages/b8/12/5b274d8a0f30c07b91b2f02cba69152600b47830fcfb465c108880fcee9c/yarl-1.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07a524d84df0c10f41e3ee918846e1974aba4ec017f990dc735aad487a0bdfdf", size = 94400, upload-time = "2025-10-06T14:08:47.855Z" }, { url = "https://files.pythonhosted.org/packages/e2/7f/df1b6949b1fa1aa9ff6de6e2631876ad4b73c4437822026e85d8acb56bb1/yarl-1.22.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1b329cb8146d7b736677a2440e422eadd775d1806a81db2d4cded80a48efc1a", size = 347545, upload-time = "2025-10-06T14:08:49.683Z" }, { url = "https://files.pythonhosted.org/packages/84/09/f92ed93bd6cd77872ab6c3462df45ca45cd058d8f1d0c9b4f54c1704429f/yarl-1.22.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:75976c6945d85dbb9ee6308cd7ff7b1fb9409380c82d6119bd778d8fcfe2931c", size = 319598, upload-time = "2025-10-06T14:08:51.215Z" }, { url = "https://files.pythonhosted.org/packages/c3/97/ac3f3feae7d522cf7ccec3d340bb0b2b61c56cb9767923df62a135092c6b/yarl-1.22.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:80ddf7a5f8c86cb3eb4bc9028b07bbbf1f08a96c5c0bc1244be5e8fefcb94147", size = 363893, upload-time = "2025-10-06T14:08:53.144Z" }, { url = "https://files.pythonhosted.org/packages/06/49/f3219097403b9c84a4d079b1d7bda62dd9b86d0d6e4428c02d46ab2c77fc/yarl-1.22.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d332fc2e3c94dad927f2112395772a4e4fedbcf8f80efc21ed7cdfae4d574fdb", size = 371240, upload-time = "2025-10-06T14:08:55.036Z" }, { url = "https://files.pythonhosted.org/packages/35/9f/06b765d45c0e44e8ecf0fe15c9eacbbde342bb5b7561c46944f107bfb6c3/yarl-1.22.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cf71bf877efeac18b38d3930594c0948c82b64547c1cf420ba48722fe5509f6", size = 346965, upload-time = "2025-10-06T14:08:56.722Z" }, { url = "https://files.pythonhosted.org/packages/c5/69/599e7cea8d0fcb1694323b0db0dda317fa3162f7b90166faddecf532166f/yarl-1.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:663e1cadaddae26be034a6ab6072449a8426ddb03d500f43daf952b74553bba0", size = 342026, upload-time = "2025-10-06T14:08:58.563Z" }, { url = "https://files.pythonhosted.org/packages/95/6f/9dfd12c8bc90fea9eab39832ee32ea48f8e53d1256252a77b710c065c89f/yarl-1.22.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6dcbb0829c671f305be48a7227918cfcd11276c2d637a8033a99a02b67bf9eda", size = 335637, upload-time = "2025-10-06T14:09:00.506Z" }, { url = "https://files.pythonhosted.org/packages/57/2e/34c5b4eb9b07e16e873db5b182c71e5f06f9b5af388cdaa97736d79dd9a6/yarl-1.22.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f0d97c18dfd9a9af4490631905a3f131a8e4c9e80a39353919e2cfed8f00aedc", size = 359082, upload-time = "2025-10-06T14:09:01.936Z" }, { url = "https://files.pythonhosted.org/packages/31/71/fa7e10fb772d273aa1f096ecb8ab8594117822f683bab7d2c5a89914c92a/yarl-1.22.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:437840083abe022c978470b942ff832c3940b2ad3734d424b7eaffcd07f76737", size = 357811, upload-time = "2025-10-06T14:09:03.445Z" }, { url = "https://files.pythonhosted.org/packages/26/da/11374c04e8e1184a6a03cf9c8f5688d3e5cec83ed6f31ad3481b3207f709/yarl-1.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a899cbd98dce6f5d8de1aad31cb712ec0a530abc0a86bd6edaa47c1090138467", size = 351223, upload-time = "2025-10-06T14:09:05.401Z" }, { url = "https://files.pythonhosted.org/packages/82/8f/e2d01f161b0c034a30410e375e191a5d27608c1f8693bab1a08b089ca096/yarl-1.22.0-cp310-cp310-win32.whl", hash = "sha256:595697f68bd1f0c1c159fcb97b661fc9c3f5db46498043555d04805430e79bea", size = 82118, upload-time = "2025-10-06T14:09:11.148Z" }, { url = "https://files.pythonhosted.org/packages/62/46/94c76196642dbeae634c7a61ba3da88cd77bed875bf6e4a8bed037505aa6/yarl-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb95a9b1adaa48e41815a55ae740cfda005758104049a640a398120bf02515ca", size = 86852, upload-time = "2025-10-06T14:09:12.958Z" }, { url = "https://files.pythonhosted.org/packages/af/af/7df4f179d3b1a6dcb9a4bd2ffbc67642746fcafdb62580e66876ce83fff4/yarl-1.22.0-cp310-cp310-win_arm64.whl", hash = "sha256:b85b982afde6df99ecc996990d4ad7ccbdbb70e2a4ba4de0aecde5922ba98a0b", size = 82012, upload-time = "2025-10-06T14:09:14.664Z" }, { url = "https://files.pythonhosted.org/packages/4d/27/5ab13fc84c76a0250afd3d26d5936349a35be56ce5785447d6c423b26d92/yarl-1.22.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ab72135b1f2db3fed3997d7e7dc1b80573c67138023852b6efb336a5eae6511", size = 141607, upload-time = "2025-10-06T14:09:16.298Z" }, { url = "https://files.pythonhosted.org/packages/6a/a1/d065d51d02dc02ce81501d476b9ed2229d9a990818332242a882d5d60340/yarl-1.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:669930400e375570189492dc8d8341301578e8493aec04aebc20d4717f899dd6", size = 94027, upload-time = "2025-10-06T14:09:17.786Z" }, { url = "https://files.pythonhosted.org/packages/c1/da/8da9f6a53f67b5106ffe902c6fa0164e10398d4e150d85838b82f424072a/yarl-1.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:792a2af6d58177ef7c19cbf0097aba92ca1b9cb3ffdd9c7470e156c8f9b5e028", size = 94963, upload-time = "2025-10-06T14:09:19.662Z" }, { url = "https://files.pythonhosted.org/packages/68/fe/2c1f674960c376e29cb0bec1249b117d11738db92a6ccc4a530b972648db/yarl-1.22.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ea66b1c11c9150f1372f69afb6b8116f2dd7286f38e14ea71a44eee9ec51b9d", size = 368406, upload-time = "2025-10-06T14:09:21.402Z" }, { url = "https://files.pythonhosted.org/packages/95/26/812a540e1c3c6418fec60e9bbd38e871eaba9545e94fa5eff8f4a8e28e1e/yarl-1.22.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3e2daa88dc91870215961e96a039ec73e4937da13cf77ce17f9cad0c18df3503", size = 336581, upload-time = "2025-10-06T14:09:22.98Z" }, { url = "https://files.pythonhosted.org/packages/0b/f5/5777b19e26fdf98563985e481f8be3d8a39f8734147a6ebf459d0dab5a6b/yarl-1.22.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba440ae430c00eee41509353628600212112cd5018d5def7e9b05ea7ac34eb65", size = 388924, upload-time = "2025-10-06T14:09:24.655Z" }, { url = "https://files.pythonhosted.org/packages/86/08/24bd2477bd59c0bbd994fe1d93b126e0472e4e3df5a96a277b0a55309e89/yarl-1.22.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e6438cc8f23a9c1478633d216b16104a586b9761db62bfacb6425bac0a36679e", size = 392890, upload-time = "2025-10-06T14:09:26.617Z" }, { url = "https://files.pythonhosted.org/packages/46/00/71b90ed48e895667ecfb1eaab27c1523ee2fa217433ed77a73b13205ca4b/yarl-1.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c52a6e78aef5cf47a98ef8e934755abf53953379b7d53e68b15ff4420e6683d", size = 365819, upload-time = "2025-10-06T14:09:28.544Z" }, { url = "https://files.pythonhosted.org/packages/30/2d/f715501cae832651d3282387c6a9236cd26bd00d0ff1e404b3dc52447884/yarl-1.22.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3b06bcadaac49c70f4c88af4ffcfbe3dc155aab3163e75777818092478bcbbe7", size = 363601, upload-time = "2025-10-06T14:09:30.568Z" }, { url = "https://files.pythonhosted.org/packages/f8/f9/a678c992d78e394e7126ee0b0e4e71bd2775e4334d00a9278c06a6cce96a/yarl-1.22.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6944b2dc72c4d7f7052683487e3677456050ff77fcf5e6204e98caf785ad1967", size = 358072, upload-time = "2025-10-06T14:09:32.528Z" }, { url = "https://files.pythonhosted.org/packages/2c/d1/b49454411a60edb6fefdcad4f8e6dbba7d8019e3a508a1c5836cba6d0781/yarl-1.22.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5372ca1df0f91a86b047d1277c2aaf1edb32d78bbcefffc81b40ffd18f027ed", size = 385311, upload-time = "2025-10-06T14:09:34.634Z" }, { url = "https://files.pythonhosted.org/packages/87/e5/40d7a94debb8448c7771a916d1861d6609dddf7958dc381117e7ba36d9e8/yarl-1.22.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:51af598701f5299012b8416486b40fceef8c26fc87dc6d7d1f6fc30609ea0aa6", size = 381094, upload-time = "2025-10-06T14:09:36.268Z" }, { url = "https://files.pythonhosted.org/packages/35/d8/611cc282502381ad855448643e1ad0538957fc82ae83dfe7762c14069e14/yarl-1.22.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b266bd01fedeffeeac01a79ae181719ff848a5a13ce10075adbefc8f1daee70e", size = 370944, upload-time = "2025-10-06T14:09:37.872Z" }, { url = "https://files.pythonhosted.org/packages/2d/df/fadd00fb1c90e1a5a8bd731fa3d3de2e165e5a3666a095b04e31b04d9cb6/yarl-1.22.0-cp311-cp311-win32.whl", hash = "sha256:a9b1ba5610a4e20f655258d5a1fdc7ebe3d837bb0e45b581398b99eb98b1f5ca", size = 81804, upload-time = "2025-10-06T14:09:39.359Z" }, { url = "https://files.pythonhosted.org/packages/b5/f7/149bb6f45f267cb5c074ac40c01c6b3ea6d8a620d34b337f6321928a1b4d/yarl-1.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:078278b9b0b11568937d9509b589ee83ef98ed6d561dfe2020e24a9fd08eaa2b", size = 86858, upload-time = "2025-10-06T14:09:41.068Z" }, { url = "https://files.pythonhosted.org/packages/2b/13/88b78b93ad3f2f0b78e13bfaaa24d11cbc746e93fe76d8c06bf139615646/yarl-1.22.0-cp311-cp311-win_arm64.whl", hash = "sha256:b6a6f620cfe13ccec221fa312139135166e47ae169f8253f72a0abc0dae94376", size = 81637, upload-time = "2025-10-06T14:09:42.712Z" }, { url = "https://files.pythonhosted.org/packages/75/ff/46736024fee3429b80a165a732e38e5d5a238721e634ab41b040d49f8738/yarl-1.22.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e340382d1afa5d32b892b3ff062436d592ec3d692aeea3bef3a5cfe11bbf8c6f", size = 142000, upload-time = "2025-10-06T14:09:44.631Z" }, { url = "https://files.pythonhosted.org/packages/5a/9a/b312ed670df903145598914770eb12de1bac44599549b3360acc96878df8/yarl-1.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f1e09112a2c31ffe8d80be1b0988fa6a18c5d5cad92a9ffbb1c04c91bfe52ad2", size = 94338, upload-time = "2025-10-06T14:09:46.372Z" }, { url = "https://files.pythonhosted.org/packages/ba/f5/0601483296f09c3c65e303d60c070a5c19fcdbc72daa061e96170785bc7d/yarl-1.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:939fe60db294c786f6b7c2d2e121576628468f65453d86b0fe36cb52f987bd74", size = 94909, upload-time = "2025-10-06T14:09:48.648Z" }, { url = "https://files.pythonhosted.org/packages/60/41/9a1fe0b73dbcefce72e46cf149b0e0a67612d60bfc90fb59c2b2efdfbd86/yarl-1.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1651bf8e0398574646744c1885a41198eba53dc8a9312b954073f845c90a8df", size = 372940, upload-time = "2025-10-06T14:09:50.089Z" }, { url = "https://files.pythonhosted.org/packages/17/7a/795cb6dfee561961c30b800f0ed616b923a2ec6258b5def2a00bf8231334/yarl-1.22.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b8a0588521a26bf92a57a1705b77b8b59044cdceccac7151bd8d229e66b8dedb", size = 345825, upload-time = "2025-10-06T14:09:52.142Z" }, { url = "https://files.pythonhosted.org/packages/d7/93/a58f4d596d2be2ae7bab1a5846c4d270b894958845753b2c606d666744d3/yarl-1.22.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42188e6a615c1a75bcaa6e150c3fe8f3e8680471a6b10150c5f7e83f47cc34d2", size = 386705, upload-time = "2025-10-06T14:09:54.128Z" }, { url = "https://files.pythonhosted.org/packages/61/92/682279d0e099d0e14d7fd2e176bd04f48de1484f56546a3e1313cd6c8e7c/yarl-1.22.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6d2cb59377d99718913ad9a151030d6f83ef420a2b8f521d94609ecc106ee82", size = 396518, upload-time = "2025-10-06T14:09:55.762Z" }, { url = "https://files.pythonhosted.org/packages/db/0f/0d52c98b8a885aeda831224b78f3be7ec2e1aa4a62091f9f9188c3c65b56/yarl-1.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50678a3b71c751d58d7908edc96d332af328839eea883bb554a43f539101277a", size = 377267, upload-time = "2025-10-06T14:09:57.958Z" }, { url = "https://files.pythonhosted.org/packages/22/42/d2685e35908cbeaa6532c1fc73e89e7f2efb5d8a7df3959ea8e37177c5a3/yarl-1.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e8fbaa7cec507aa24ea27a01456e8dd4b6fab829059b69844bd348f2d467124", size = 365797, upload-time = "2025-10-06T14:09:59.527Z" }, { url = "https://files.pythonhosted.org/packages/a2/83/cf8c7bcc6355631762f7d8bdab920ad09b82efa6b722999dfb05afa6cfac/yarl-1.22.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:433885ab5431bc3d3d4f2f9bd15bfa1614c522b0f1405d62c4f926ccd69d04fa", size = 365535, upload-time = "2025-10-06T14:10:01.139Z" }, { url = "https://files.pythonhosted.org/packages/25/e1/5302ff9b28f0c59cac913b91fe3f16c59a033887e57ce9ca5d41a3a94737/yarl-1.22.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b790b39c7e9a4192dc2e201a282109ed2985a1ddbd5ac08dc56d0e121400a8f7", size = 382324, upload-time = "2025-10-06T14:10:02.756Z" }, { url = "https://files.pythonhosted.org/packages/bf/cd/4617eb60f032f19ae3a688dc990d8f0d89ee0ea378b61cac81ede3e52fae/yarl-1.22.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31f0b53913220599446872d757257be5898019c85e7971599065bc55065dc99d", size = 383803, upload-time = "2025-10-06T14:10:04.552Z" }, { url = "https://files.pythonhosted.org/packages/59/65/afc6e62bb506a319ea67b694551dab4a7e6fb7bf604e9bd9f3e11d575fec/yarl-1.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a49370e8f711daec68d09b821a34e1167792ee2d24d405cbc2387be4f158b520", size = 374220, upload-time = "2025-10-06T14:10:06.489Z" }, { url = "https://files.pythonhosted.org/packages/e7/3d/68bf18d50dc674b942daec86a9ba922d3113d8399b0e52b9897530442da2/yarl-1.22.0-cp312-cp312-win32.whl", hash = "sha256:70dfd4f241c04bd9239d53b17f11e6ab672b9f1420364af63e8531198e3f5fe8", size = 81589, upload-time = "2025-10-06T14:10:09.254Z" }, { url = "https://files.pythonhosted.org/packages/c8/9a/6ad1a9b37c2f72874f93e691b2e7ecb6137fb2b899983125db4204e47575/yarl-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:8884d8b332a5e9b88e23f60bb166890009429391864c685e17bd73a9eda9105c", size = 87213, upload-time = "2025-10-06T14:10:11.369Z" }, { url = "https://files.pythonhosted.org/packages/44/c5/c21b562d1680a77634d748e30c653c3ca918beb35555cff24986fff54598/yarl-1.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea70f61a47f3cc93bdf8b2f368ed359ef02a01ca6393916bc8ff877427181e74", size = 81330, upload-time = "2025-10-06T14:10:13.112Z" }, { url = "https://files.pythonhosted.org/packages/ea/f3/d67de7260456ee105dc1d162d43a019ecad6b91e2f51809d6cddaa56690e/yarl-1.22.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8dee9c25c74997f6a750cd317b8ca63545169c098faee42c84aa5e506c819b53", size = 139980, upload-time = "2025-10-06T14:10:14.601Z" }, { url = "https://files.pythonhosted.org/packages/01/88/04d98af0b47e0ef42597b9b28863b9060bb515524da0a65d5f4db160b2d5/yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01e73b85a5434f89fc4fe27dcda2aff08ddf35e4d47bbbea3bdcd25321af538a", size = 93424, upload-time = "2025-10-06T14:10:16.115Z" }, { url = "https://files.pythonhosted.org/packages/18/91/3274b215fd8442a03975ce6bee5fe6aa57a8326b29b9d3d56234a1dca244/yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22965c2af250d20c873cdbee8ff958fb809940aeb2e74ba5f20aaf6b7ac8c70c", size = 93821, upload-time = "2025-10-06T14:10:17.993Z" }, { url = "https://files.pythonhosted.org/packages/61/3a/caf4e25036db0f2da4ca22a353dfeb3c9d3c95d2761ebe9b14df8fc16eb0/yarl-1.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4f15793aa49793ec8d1c708ab7f9eded1aa72edc5174cae703651555ed1b601", size = 373243, upload-time = "2025-10-06T14:10:19.44Z" }, { url = "https://files.pythonhosted.org/packages/6e/9e/51a77ac7516e8e7803b06e01f74e78649c24ee1021eca3d6a739cb6ea49c/yarl-1.22.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5542339dcf2747135c5c85f68680353d5cb9ffd741c0f2e8d832d054d41f35a", size = 342361, upload-time = "2025-10-06T14:10:21.124Z" }, { url = "https://files.pythonhosted.org/packages/d4/f8/33b92454789dde8407f156c00303e9a891f1f51a0330b0fad7c909f87692/yarl-1.22.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c401e05ad47a75869c3ab3e35137f8468b846770587e70d71e11de797d113df", size = 387036, upload-time = "2025-10-06T14:10:22.902Z" }, { url = "https://files.pythonhosted.org/packages/d9/9a/c5db84ea024f76838220280f732970aa4ee154015d7f5c1bfb60a267af6f/yarl-1.22.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:243dda95d901c733f5b59214d28b0120893d91777cb8aa043e6ef059d3cddfe2", size = 397671, upload-time = "2025-10-06T14:10:24.523Z" }, { url = "https://files.pythonhosted.org/packages/11/c9/cd8538dc2e7727095e0c1d867bad1e40c98f37763e6d995c1939f5fdc7b1/yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bec03d0d388060058f5d291a813f21c011041938a441c593374da6077fe21b1b", size = 377059, upload-time = "2025-10-06T14:10:26.406Z" }, { url = "https://files.pythonhosted.org/packages/a1/b9/ab437b261702ced75122ed78a876a6dec0a1b0f5e17a4ac7a9a2482d8abe/yarl-1.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0748275abb8c1e1e09301ee3cf90c8a99678a4e92e4373705f2a2570d581273", size = 365356, upload-time = "2025-10-06T14:10:28.461Z" }, { url = "https://files.pythonhosted.org/packages/b2/9d/8e1ae6d1d008a9567877b08f0ce4077a29974c04c062dabdb923ed98e6fe/yarl-1.22.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:47fdb18187e2a4e18fda2c25c05d8251a9e4a521edaed757fef033e7d8498d9a", size = 361331, upload-time = "2025-10-06T14:10:30.541Z" }, { url = "https://files.pythonhosted.org/packages/ca/5a/09b7be3905962f145b73beb468cdd53db8aa171cf18c80400a54c5b82846/yarl-1.22.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c7044802eec4524fde550afc28edda0dd5784c4c45f0be151a2d3ba017daca7d", size = 382590, upload-time = "2025-10-06T14:10:33.352Z" }, { url = "https://files.pythonhosted.org/packages/aa/7f/59ec509abf90eda5048b0bc3e2d7b5099dffdb3e6b127019895ab9d5ef44/yarl-1.22.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:139718f35149ff544caba20fce6e8a2f71f1e39b92c700d8438a0b1d2a631a02", size = 385316, upload-time = "2025-10-06T14:10:35.034Z" }, { url = "https://files.pythonhosted.org/packages/e5/84/891158426bc8036bfdfd862fabd0e0fa25df4176ec793e447f4b85cf1be4/yarl-1.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1b51bebd221006d3d2f95fbe124b22b247136647ae5dcc8c7acafba66e5ee67", size = 374431, upload-time = "2025-10-06T14:10:37.76Z" }, { url = "https://files.pythonhosted.org/packages/bb/49/03da1580665baa8bef5e8ed34c6df2c2aca0a2f28bf397ed238cc1bbc6f2/yarl-1.22.0-cp313-cp313-win32.whl", hash = "sha256:d3e32536234a95f513bd374e93d717cf6b2231a791758de6c509e3653f234c95", size = 81555, upload-time = "2025-10-06T14:10:39.649Z" }, { url = "https://files.pythonhosted.org/packages/9a/ee/450914ae11b419eadd067c6183ae08381cfdfcb9798b90b2b713bbebddda/yarl-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:47743b82b76d89a1d20b83e60d5c20314cbd5ba2befc9cda8f28300c4a08ed4d", size = 86965, upload-time = "2025-10-06T14:10:41.313Z" }, { url = "https://files.pythonhosted.org/packages/98/4d/264a01eae03b6cf629ad69bae94e3b0e5344741e929073678e84bf7a3e3b/yarl-1.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d0fcda9608875f7d052eff120c7a5da474a6796fe4d83e152e0e4d42f6d1a9b", size = 81205, upload-time = "2025-10-06T14:10:43.167Z" }, { url = "https://files.pythonhosted.org/packages/88/fc/6908f062a2f77b5f9f6d69cecb1747260831ff206adcbc5b510aff88df91/yarl-1.22.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:719ae08b6972befcba4310e49edb1161a88cdd331e3a694b84466bd938a6ab10", size = 146209, upload-time = "2025-10-06T14:10:44.643Z" }, { url = "https://files.pythonhosted.org/packages/65/47/76594ae8eab26210b4867be6f49129861ad33da1f1ebdf7051e98492bf62/yarl-1.22.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47d8a5c446df1c4db9d21b49619ffdba90e77c89ec6e283f453856c74b50b9e3", size = 95966, upload-time = "2025-10-06T14:10:46.554Z" }, { url = "https://files.pythonhosted.org/packages/ab/ce/05e9828a49271ba6b5b038b15b3934e996980dd78abdfeb52a04cfb9467e/yarl-1.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cfebc0ac8333520d2d0423cbbe43ae43c8838862ddb898f5ca68565e395516e9", size = 97312, upload-time = "2025-10-06T14:10:48.007Z" }, { url = "https://files.pythonhosted.org/packages/d1/c5/7dffad5e4f2265b29c9d7ec869c369e4223166e4f9206fc2243ee9eea727/yarl-1.22.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4398557cbf484207df000309235979c79c4356518fd5c99158c7d38203c4da4f", size = 361967, upload-time = "2025-10-06T14:10:49.997Z" }, { url = "https://files.pythonhosted.org/packages/50/b2/375b933c93a54bff7fc041e1a6ad2c0f6f733ffb0c6e642ce56ee3b39970/yarl-1.22.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2ca6fd72a8cd803be290d42f2dec5cdcd5299eeb93c2d929bf060ad9efaf5de0", size = 323949, upload-time = "2025-10-06T14:10:52.004Z" }, { url = "https://files.pythonhosted.org/packages/66/50/bfc2a29a1d78644c5a7220ce2f304f38248dc94124a326794e677634b6cf/yarl-1.22.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca1f59c4e1ab6e72f0a23c13fca5430f889634166be85dbf1013683e49e3278e", size = 361818, upload-time = "2025-10-06T14:10:54.078Z" }, { url = "https://files.pythonhosted.org/packages/46/96/f3941a46af7d5d0f0498f86d71275696800ddcdd20426298e572b19b91ff/yarl-1.22.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c5010a52015e7c70f86eb967db0f37f3c8bd503a695a49f8d45700144667708", size = 372626, upload-time = "2025-10-06T14:10:55.767Z" }, { url = "https://files.pythonhosted.org/packages/c1/42/8b27c83bb875cd89448e42cd627e0fb971fa1675c9ec546393d18826cb50/yarl-1.22.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d7672ecf7557476642c88497c2f8d8542f8e36596e928e9bcba0e42e1e7d71f", size = 341129, upload-time = "2025-10-06T14:10:57.985Z" }, { url = "https://files.pythonhosted.org/packages/49/36/99ca3122201b382a3cf7cc937b95235b0ac944f7e9f2d5331d50821ed352/yarl-1.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b7c88eeef021579d600e50363e0b6ee4f7f6f728cd3486b9d0f3ee7b946398d", size = 346776, upload-time = "2025-10-06T14:10:59.633Z" }, { url = "https://files.pythonhosted.org/packages/85/b4/47328bf996acd01a4c16ef9dcd2f59c969f495073616586f78cd5f2efb99/yarl-1.22.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f4afb5c34f2c6fecdcc182dfcfc6af6cccf1aa923eed4d6a12e9d96904e1a0d8", size = 334879, upload-time = "2025-10-06T14:11:01.454Z" }, { url = "https://files.pythonhosted.org/packages/c2/ad/b77d7b3f14a4283bffb8e92c6026496f6de49751c2f97d4352242bba3990/yarl-1.22.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:59c189e3e99a59cf8d83cbb31d4db02d66cda5a1a4374e8a012b51255341abf5", size = 350996, upload-time = "2025-10-06T14:11:03.452Z" }, { url = "https://files.pythonhosted.org/packages/81/c8/06e1d69295792ba54d556f06686cbd6a7ce39c22307100e3fb4a2c0b0a1d/yarl-1.22.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5a3bf7f62a289fa90f1990422dc8dff5a458469ea71d1624585ec3a4c8d6960f", size = 356047, upload-time = "2025-10-06T14:11:05.115Z" }, { url = "https://files.pythonhosted.org/packages/4b/b8/4c0e9e9f597074b208d18cef227d83aac36184bfbc6eab204ea55783dbc5/yarl-1.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:de6b9a04c606978fdfe72666fa216ffcf2d1a9f6a381058d4378f8d7b1e5de62", size = 342947, upload-time = "2025-10-06T14:11:08.137Z" }, { url = "https://files.pythonhosted.org/packages/e0/e5/11f140a58bf4c6ad7aca69a892bff0ee638c31bea4206748fc0df4ebcb3a/yarl-1.22.0-cp313-cp313t-win32.whl", hash = "sha256:1834bb90991cc2999f10f97f5f01317f99b143284766d197e43cd5b45eb18d03", size = 86943, upload-time = "2025-10-06T14:11:10.284Z" }, { url = "https://files.pythonhosted.org/packages/31/74/8b74bae38ed7fe6793d0c15a0c8207bbb819cf287788459e5ed230996cdd/yarl-1.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff86011bd159a9d2dfc89c34cfd8aff12875980e3bd6a39ff097887520e60249", size = 93715, upload-time = "2025-10-06T14:11:11.739Z" }, { url = "https://files.pythonhosted.org/packages/69/66/991858aa4b5892d57aef7ee1ba6b4d01ec3b7eb3060795d34090a3ca3278/yarl-1.22.0-cp313-cp313t-win_arm64.whl", hash = "sha256:7861058d0582b847bc4e3a4a4c46828a410bca738673f35a29ba3ca5db0b473b", size = 83857, upload-time = "2025-10-06T14:11:13.586Z" }, { url = "https://files.pythonhosted.org/packages/46/b3/e20ef504049f1a1c54a814b4b9bed96d1ac0e0610c3b4da178f87209db05/yarl-1.22.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:34b36c2c57124530884d89d50ed2c1478697ad7473efd59cfd479945c95650e4", size = 140520, upload-time = "2025-10-06T14:11:15.465Z" }, { url = "https://files.pythonhosted.org/packages/e4/04/3532d990fdbab02e5ede063676b5c4260e7f3abea2151099c2aa745acc4c/yarl-1.22.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0dd9a702591ca2e543631c2a017e4a547e38a5c0f29eece37d9097e04a7ac683", size = 93504, upload-time = "2025-10-06T14:11:17.106Z" }, { url = "https://files.pythonhosted.org/packages/11/63/ff458113c5c2dac9a9719ac68ee7c947cb621432bcf28c9972b1c0e83938/yarl-1.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:594fcab1032e2d2cc3321bb2e51271e7cd2b516c7d9aee780ece81b07ff8244b", size = 94282, upload-time = "2025-10-06T14:11:19.064Z" }, { url = "https://files.pythonhosted.org/packages/a7/bc/315a56aca762d44a6aaaf7ad253f04d996cb6b27bad34410f82d76ea8038/yarl-1.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d7a87a78d46a2e3d5b72587ac14b4c16952dd0887dbb051451eceac774411e", size = 372080, upload-time = "2025-10-06T14:11:20.996Z" }, { url = "https://files.pythonhosted.org/packages/3f/3f/08e9b826ec2e099ea6e7c69a61272f4f6da62cb5b1b63590bb80ca2e4a40/yarl-1.22.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:852863707010316c973162e703bddabec35e8757e67fcb8ad58829de1ebc8590", size = 338696, upload-time = "2025-10-06T14:11:22.847Z" }, { url = "https://files.pythonhosted.org/packages/e3/9f/90360108e3b32bd76789088e99538febfea24a102380ae73827f62073543/yarl-1.22.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:131a085a53bfe839a477c0845acf21efc77457ba2bcf5899618136d64f3303a2", size = 387121, upload-time = "2025-10-06T14:11:24.889Z" }, { url = "https://files.pythonhosted.org/packages/98/92/ab8d4657bd5b46a38094cfaea498f18bb70ce6b63508fd7e909bd1f93066/yarl-1.22.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:078a8aefd263f4d4f923a9677b942b445a2be970ca24548a8102689a3a8ab8da", size = 394080, upload-time = "2025-10-06T14:11:27.307Z" }, { url = "https://files.pythonhosted.org/packages/f5/e7/d8c5a7752fef68205296201f8ec2bf718f5c805a7a7e9880576c67600658/yarl-1.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca03b91c323036913993ff5c738d0842fc9c60c4648e5c8d98331526df89784", size = 372661, upload-time = "2025-10-06T14:11:29.387Z" }, { url = "https://files.pythonhosted.org/packages/b6/2e/f4d26183c8db0bb82d491b072f3127fb8c381a6206a3a56332714b79b751/yarl-1.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:68986a61557d37bb90d3051a45b91fa3d5c516d177dfc6dd6f2f436a07ff2b6b", size = 364645, upload-time = "2025-10-06T14:11:31.423Z" }, { url = "https://files.pythonhosted.org/packages/80/7c/428e5812e6b87cd00ee8e898328a62c95825bf37c7fa87f0b6bb2ad31304/yarl-1.22.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4792b262d585ff0dff6bcb787f8492e40698443ec982a3568c2096433660c694", size = 355361, upload-time = "2025-10-06T14:11:33.055Z" }, { url = "https://files.pythonhosted.org/packages/ec/2a/249405fd26776f8b13c067378ef4d7dd49c9098d1b6457cdd152a99e96a9/yarl-1.22.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ebd4549b108d732dba1d4ace67614b9545b21ece30937a63a65dd34efa19732d", size = 381451, upload-time = "2025-10-06T14:11:35.136Z" }, { url = "https://files.pythonhosted.org/packages/67/a8/fb6b1adbe98cf1e2dd9fad71003d3a63a1bc22459c6e15f5714eb9323b93/yarl-1.22.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f87ac53513d22240c7d59203f25cc3beac1e574c6cd681bbfd321987b69f95fd", size = 383814, upload-time = "2025-10-06T14:11:37.094Z" }, { url = "https://files.pythonhosted.org/packages/d9/f9/3aa2c0e480fb73e872ae2814c43bc1e734740bb0d54e8cb2a95925f98131/yarl-1.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:22b029f2881599e2f1b06f8f1db2ee63bd309e2293ba2d566e008ba12778b8da", size = 370799, upload-time = "2025-10-06T14:11:38.83Z" }, { url = "https://files.pythonhosted.org/packages/50/3c/af9dba3b8b5eeb302f36f16f92791f3ea62e3f47763406abf6d5a4a3333b/yarl-1.22.0-cp314-cp314-win32.whl", hash = "sha256:6a635ea45ba4ea8238463b4f7d0e721bad669f80878b7bfd1f89266e2ae63da2", size = 82990, upload-time = "2025-10-06T14:11:40.624Z" }, { url = "https://files.pythonhosted.org/packages/ac/30/ac3a0c5bdc1d6efd1b41fa24d4897a4329b3b1e98de9449679dd327af4f0/yarl-1.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:0d6e6885777af0f110b0e5d7e5dda8b704efed3894da26220b7f3d887b839a79", size = 88292, upload-time = "2025-10-06T14:11:42.578Z" }, { url = "https://files.pythonhosted.org/packages/df/0a/227ab4ff5b998a1b7410abc7b46c9b7a26b0ca9e86c34ba4b8d8bc7c63d5/yarl-1.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:8218f4e98d3c10d683584cb40f0424f4b9fd6e95610232dd75e13743b070ee33", size = 82888, upload-time = "2025-10-06T14:11:44.863Z" }, { url = "https://files.pythonhosted.org/packages/06/5e/a15eb13db90abd87dfbefb9760c0f3f257ac42a5cac7e75dbc23bed97a9f/yarl-1.22.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45c2842ff0e0d1b35a6bf1cd6c690939dacb617a70827f715232b2e0494d55d1", size = 146223, upload-time = "2025-10-06T14:11:46.796Z" }, { url = "https://files.pythonhosted.org/packages/18/82/9665c61910d4d84f41a5bf6837597c89e665fa88aa4941080704645932a9/yarl-1.22.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d947071e6ebcf2e2bee8fce76e10faca8f7a14808ca36a910263acaacef08eca", size = 95981, upload-time = "2025-10-06T14:11:48.845Z" }, { url = "https://files.pythonhosted.org/packages/5d/9a/2f65743589809af4d0a6d3aa749343c4b5f4c380cc24a8e94a3c6625a808/yarl-1.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:334b8721303e61b00019474cc103bdac3d7b1f65e91f0bfedeec2d56dfe74b53", size = 97303, upload-time = "2025-10-06T14:11:50.897Z" }, { url = "https://files.pythonhosted.org/packages/b0/ab/5b13d3e157505c43c3b43b5a776cbf7b24a02bc4cccc40314771197e3508/yarl-1.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e7ce67c34138a058fd092f67d07a72b8e31ff0c9236e751957465a24b28910c", size = 361820, upload-time = "2025-10-06T14:11:52.549Z" }, { url = "https://files.pythonhosted.org/packages/fb/76/242a5ef4677615cf95330cfc1b4610e78184400699bdda0acb897ef5e49a/yarl-1.22.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d77e1b2c6d04711478cb1c4ab90db07f1609ccf06a287d5607fcd90dc9863acf", size = 323203, upload-time = "2025-10-06T14:11:54.225Z" }, { url = "https://files.pythonhosted.org/packages/8c/96/475509110d3f0153b43d06164cf4195c64d16999e0c7e2d8a099adcd6907/yarl-1.22.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4647674b6150d2cae088fc07de2738a84b8bcedebef29802cf0b0a82ab6face", size = 363173, upload-time = "2025-10-06T14:11:56.069Z" }, { url = "https://files.pythonhosted.org/packages/c9/66/59db471aecfbd559a1fd48aedd954435558cd98c7d0da8b03cc6c140a32c/yarl-1.22.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efb07073be061c8f79d03d04139a80ba33cbd390ca8f0297aae9cce6411e4c6b", size = 373562, upload-time = "2025-10-06T14:11:58.783Z" }, { url = "https://files.pythonhosted.org/packages/03/1f/c5d94abc91557384719da10ff166b916107c1b45e4d0423a88457071dd88/yarl-1.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51ac5435758ba97ad69617e13233da53908beccc6cfcd6c34bbed8dcbede486", size = 339828, upload-time = "2025-10-06T14:12:00.686Z" }, { url = "https://files.pythonhosted.org/packages/5f/97/aa6a143d3afba17b6465733681c70cf175af89f76ec8d9286e08437a7454/yarl-1.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33e32a0dd0c8205efa8e83d04fc9f19313772b78522d1bdc7d9aed706bfd6138", size = 347551, upload-time = "2025-10-06T14:12:02.628Z" }, { url = "https://files.pythonhosted.org/packages/43/3c/45a2b6d80195959239a7b2a8810506d4eea5487dce61c2a3393e7fc3c52e/yarl-1.22.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:bf4a21e58b9cde0e401e683ebd00f6ed30a06d14e93f7c8fd059f8b6e8f87b6a", size = 334512, upload-time = "2025-10-06T14:12:04.871Z" }, { url = "https://files.pythonhosted.org/packages/86/a0/c2ab48d74599c7c84cb104ebd799c5813de252bea0f360ffc29d270c2caa/yarl-1.22.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e4b582bab49ac33c8deb97e058cd67c2c50dac0dd134874106d9c774fd272529", size = 352400, upload-time = "2025-10-06T14:12:06.624Z" }, { url = "https://files.pythonhosted.org/packages/32/75/f8919b2eafc929567d3d8411f72bdb1a2109c01caaab4ebfa5f8ffadc15b/yarl-1.22.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0b5bcc1a9c4839e7e30b7b30dd47fe5e7e44fb7054ec29b5bb8d526aa1041093", size = 357140, upload-time = "2025-10-06T14:12:08.362Z" }, { url = "https://files.pythonhosted.org/packages/cf/72/6a85bba382f22cf78add705d8c3731748397d986e197e53ecc7835e76de7/yarl-1.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c0232bce2170103ec23c454e54a57008a9a72b5d1c3105dc2496750da8cfa47c", size = 341473, upload-time = "2025-10-06T14:12:10.994Z" }, { url = "https://files.pythonhosted.org/packages/35/18/55e6011f7c044dc80b98893060773cefcfdbf60dfefb8cb2f58b9bacbd83/yarl-1.22.0-cp314-cp314t-win32.whl", hash = "sha256:8009b3173bcd637be650922ac455946197d858b3630b6d8787aa9e5c4564533e", size = 89056, upload-time = "2025-10-06T14:12:13.317Z" }, { url = "https://files.pythonhosted.org/packages/f9/86/0f0dccb6e59a9e7f122c5afd43568b1d31b8ab7dda5f1b01fb5c7025c9a9/yarl-1.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9fb17ea16e972c63d25d4a97f016d235c78dd2344820eb35bc034bc32012ee27", size = 96292, upload-time = "2025-10-06T14:12:15.398Z" }, { url = "https://files.pythonhosted.org/packages/48/b7/503c98092fb3b344a179579f55814b613c1fbb1c23b3ec14a7b008a66a6e/yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1", size = 85171, upload-time = "2025-10-06T14:12:16.935Z" }, { url = "https://files.pythonhosted.org/packages/94/fd/6480106702a79bcceda5fd9c63cb19a04a6506bd5ce7fd8d9b63742f0021/yarl-1.22.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3aa27acb6de7a23785d81557577491f6c38a5209a254d1191519d07d8fe51748", size = 141301, upload-time = "2025-10-06T14:12:19.01Z" }, { url = "https://files.pythonhosted.org/packages/42/e1/6d95d21b17a93e793e4ec420a925fe1f6a9342338ca7a563ed21129c0990/yarl-1.22.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:af74f05666a5e531289cb1cc9c883d1de2088b8e5b4de48004e5ca8a830ac859", size = 93864, upload-time = "2025-10-06T14:12:21.05Z" }, { url = "https://files.pythonhosted.org/packages/32/58/b8055273c203968e89808413ea4c984988b6649baabf10f4522e67c22d2f/yarl-1.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:62441e55958977b8167b2709c164c91a6363e25da322d87ae6dd9c6019ceecf9", size = 94706, upload-time = "2025-10-06T14:12:23.287Z" }, { url = "https://files.pythonhosted.org/packages/18/91/d7bfbc28a88c2895ecd0da6a874def0c147de78afc52c773c28e1aa233a3/yarl-1.22.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b580e71cac3f8113d3135888770903eaf2f507e9421e5697d6ee6d8cd1c7f054", size = 347100, upload-time = "2025-10-06T14:12:28.527Z" }, { url = "https://files.pythonhosted.org/packages/bd/e8/37a1e7b99721c0564b1fc7b0a4d1f595ef6fb8060d82ca61775b644185f7/yarl-1.22.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e81fda2fb4a07eda1a2252b216aa0df23ebcd4d584894e9612e80999a78fd95b", size = 318902, upload-time = "2025-10-06T14:12:30.528Z" }, { url = "https://files.pythonhosted.org/packages/1c/ef/34724449d7ef2db4f22df644f2dac0b8a275d20f585e526937b3ae47b02d/yarl-1.22.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:99b6fc1d55782461b78221e95fc357b47ad98b041e8e20f47c1411d0aacddc60", size = 363302, upload-time = "2025-10-06T14:12:32.295Z" }, { url = "https://files.pythonhosted.org/packages/8a/04/88a39a5dad39889f192cce8d66cc4c58dbeca983e83f9b6bf23822a7ed91/yarl-1.22.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:088e4e08f033db4be2ccd1f34cf29fe994772fb54cfe004bbf54db320af56890", size = 370816, upload-time = "2025-10-06T14:12:34.01Z" }, { url = "https://files.pythonhosted.org/packages/6b/1f/5e895e547129413f56c76be2c3ce4b96c797d2d0ff3e16a817d9269b12e6/yarl-1.22.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e4e1f6f0b4da23e61188676e3ed027ef0baa833a2e633c29ff8530800edccba", size = 346465, upload-time = "2025-10-06T14:12:35.977Z" }, { url = "https://files.pythonhosted.org/packages/11/13/a750e9fd6f9cc9ed3a52a70fe58ffe505322f0efe0d48e1fd9ffe53281f5/yarl-1.22.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:84fc3ec96fce86ce5aa305eb4aa9358279d1aa644b71fab7b8ed33fe3ba1a7ca", size = 341506, upload-time = "2025-10-06T14:12:37.788Z" }, { url = "https://files.pythonhosted.org/packages/3c/67/bb6024de76e7186611ebe626aec5b71a2d2ecf9453e795f2dbd80614784c/yarl-1.22.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5dbeefd6ca588b33576a01b0ad58aa934bc1b41ef89dee505bf2932b22ddffba", size = 335030, upload-time = "2025-10-06T14:12:39.775Z" }, { url = "https://files.pythonhosted.org/packages/a2/be/50b38447fd94a7992996a62b8b463d0579323fcfc08c61bdba949eef8a5d/yarl-1.22.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:14291620375b1060613f4aab9ebf21850058b6b1b438f386cc814813d901c60b", size = 358560, upload-time = "2025-10-06T14:12:41.547Z" }, { url = "https://files.pythonhosted.org/packages/e2/89/c020b6f547578c4e3dbb6335bf918f26e2f34ad0d1e515d72fd33ac0c635/yarl-1.22.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:a4fcfc8eb2c34148c118dfa02e6427ca278bfd0f3df7c5f99e33d2c0e81eae3e", size = 357290, upload-time = "2025-10-06T14:12:43.861Z" }, { url = "https://files.pythonhosted.org/packages/8c/52/c49a619ee35a402fa3a7019a4fa8d26878fec0d1243f6968bbf516789578/yarl-1.22.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:029866bde8d7b0878b9c160e72305bbf0a7342bcd20b9999381704ae03308dc8", size = 350700, upload-time = "2025-10-06T14:12:46.868Z" }, { url = "https://files.pythonhosted.org/packages/ab/c9/f5042d87777bf6968435f04a2bbb15466b2f142e6e47fa4f34d1a3f32f0c/yarl-1.22.0-cp39-cp39-win32.whl", hash = "sha256:4dcc74149ccc8bba31ce1944acee24813e93cfdee2acda3c172df844948ddf7b", size = 82323, upload-time = "2025-10-06T14:12:48.633Z" }, { url = "https://files.pythonhosted.org/packages/fd/58/d00f7cad9eba20c4eefac2682f34661d1d1b3a942fc0092eb60e78cfb733/yarl-1.22.0-cp39-cp39-win_amd64.whl", hash = "sha256:10619d9fdee46d20edc49d3479e2f8269d0779f1b031e6f7c2aa1c76be04b7ed", size = 87145, upload-time = "2025-10-06T14:12:50.241Z" }, { url = "https://files.pythonhosted.org/packages/c2/a3/70904f365080780d38b919edd42d224b8c4ce224a86950d2eaa2a24366ad/yarl-1.22.0-cp39-cp39-win_arm64.whl", hash = "sha256:dd7afd3f8b0bfb4e0d9fc3c31bfe8a4ec7debe124cfd90619305def3c8ca8cd2", size = 82173, upload-time = "2025-10-06T14:12:51.869Z" }, { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814, upload-time = "2025-10-06T14:12:53.872Z" }, ] [[package]] name = "yarl" version = "1.23.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.14' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and python_full_version < '3.14' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and python_full_version < '3.14' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra != 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra == 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", "python_full_version >= '3.10' and extra != 'extra-9-anthropic-mcp' and extra != 'group-9-anthropic-pydantic-v1' and extra != 'group-9-anthropic-pydantic-v2'", ] dependencies = [ { name = "idna", marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "multidict", marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, { name = "propcache", marker = "python_full_version >= '3.10' or (extra == 'extra-9-anthropic-mcp' and extra == 'group-9-anthropic-pydantic-v1') or (extra == 'group-9-anthropic-pydantic-v1' and extra == 'group-9-anthropic-pydantic-v2')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/23/6e/beb1beec874a72f23815c1434518bfc4ed2175065173fb138c3705f658d4/yarl-1.23.0.tar.gz", hash = "sha256:53b1ea6ca88ebd4420379c330aea57e258408dd0df9af0992e5de2078dc9f5d5", size = 194676, upload-time = "2026-03-01T22:07:53.373Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/8b/0d/9cc638702f6fc3c7a3685bcc8cf2a9ed7d6206e932a49f5242658047ef51/yarl-1.23.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cff6d44cb13d39db2663a22b22305d10855efa0fa8015ddeacc40bc59b9d8107", size = 123764, upload-time = "2026-03-01T22:04:09.7Z" }, { url = "https://files.pythonhosted.org/packages/7a/35/5a553687c5793df5429cd1db45909d4f3af7eee90014888c208d086a44f0/yarl-1.23.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e4c53f8347cd4200f0d70a48ad059cabaf24f5adc6ba08622a23423bc7efa10d", size = 86282, upload-time = "2026-03-01T22:04:11.892Z" }, { url = "https://files.pythonhosted.org/packages/68/2e/c5a2234238f8ce37a8312b52801ee74117f576b1539eec8404a480434acc/yarl-1.23.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a6940a074fb3c48356ed0158a3ca5699c955ee4185b4d7d619be3c327143e05", size = 86053, upload-time = "2026-03-01T22:04:13.292Z" }, { url = "https://files.pythonhosted.org/packages/74/3f/bbd8ff36fb038622797ffbaf7db314918bb4d76f1cc8a4f9ca7a55fe5195/yarl-1.23.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ed5f69ce7be7902e5c70ea19eb72d20abf7d725ab5d49777d696e32d4fc1811d", size = 99395, upload-time = "2026-03-01T22:04:15.133Z" }, { url = "https://files.pythonhosted.org/packages/77/04/9516bc4e269d2a3ec9c6779fcdeac51ce5b3a9b0156f06ac7152e5bba864/yarl-1.23.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:389871e65468400d6283c0308e791a640b5ab5c83bcee02a2f51295f95e09748", size = 92143, upload-time = "2026-03-01T22:04:16.829Z" }, { url = "https://files.pythonhosted.org/packages/c7/63/88802d1f6b1cb1fc67d67a58cd0cf8a1790de4ce7946e434240f1d60ab4a/yarl-1.23.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dda608c88cf709b1d406bdfcd84d8d63cff7c9e577a403c6108ce8ce9dcc8764", size = 107643, upload-time = "2026-03-01T22:04:18.519Z" }, { url = "https://files.pythonhosted.org/packages/8e/db/4f9b838f4d8bdd6f0f385aed8bbf21c71ed11a0b9983305c302cbd557815/yarl-1.23.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8c4fe09e0780c6c3bf2b7d4af02ee2394439d11a523bbcf095cf4747c2932007", size = 108700, upload-time = "2026-03-01T22:04:20.373Z" }, { url = "https://files.pythonhosted.org/packages/50/12/95a1d33f04a79c402664070d43b8b9f72dc18914e135b345b611b0b1f8cc/yarl-1.23.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:31c9921eb8bd12633b41ad27686bbb0b1a2a9b8452bfdf221e34f311e9942ed4", size = 102769, upload-time = "2026-03-01T22:04:23.055Z" }, { url = "https://files.pythonhosted.org/packages/86/65/91a0285f51321369fd1a8308aa19207520c5f0587772cfc2e03fc2467e90/yarl-1.23.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5f10fd85e4b75967468af655228fbfd212bdf66db1c0d135065ce288982eda26", size = 101114, upload-time = "2026-03-01T22:04:25.031Z" }, { url = "https://files.pythonhosted.org/packages/58/80/c7c8244fc3e5bc483dc71a09560f43b619fab29301a0f0a8f936e42865c7/yarl-1.23.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dbf507e9ef5688bada447a24d68b4b58dd389ba93b7afc065a2ba892bea54769", size = 98883, upload-time = "2026-03-01T22:04:27.281Z" }, { url = "https://files.pythonhosted.org/packages/86/e7/71ca9cc9ca79c0b7d491216177d1aed559d632947b8ffb0ee60f7d8b23e3/yarl-1.23.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:85e9beda1f591bc73e77ea1c51965c68e98dafd0fec72cdd745f77d727466716", size = 94172, upload-time = "2026-03-01T22:04:28.554Z" }, { url = "https://files.pythonhosted.org/packages/6a/3f/6c6c8a0fe29c26fb2db2e8d32195bb84ec1bfb8f1d32e7f73b787fcf349b/yarl-1.23.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:0e1fdaa14ef51366d7757b45bde294e95f6c8c049194e793eedb8387c86d5993", size = 107010, upload-time = "2026-03-01T22:04:30.385Z" }, { url = "https://files.pythonhosted.org/packages/56/38/12730c05e5ad40a76374d440ed8b0899729a96c250516d91c620a6e38fc2/yarl-1.23.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:75e3026ab649bf48f9a10c0134512638725b521340293f202a69b567518d94e0", size = 100285, upload-time = "2026-03-01T22:04:31.752Z" }, { url = "https://files.pythonhosted.org/packages/34/92/6a7be9239f2347234e027284e7a5f74b1140cc86575e7b469d13fba1ebfe/yarl-1.23.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:80e6d33a3d42a7549b409f199857b4fb54e2103fc44fb87605b6663b7a7ff750", size = 108230, upload-time = "2026-03-01T22:04:33.844Z" }, { url = "https://files.pythonhosted.org/packages/5e/81/4aebccfa9376bd98b9d8bfad20621a57d3e8cfc5b8631c1fa5f62cdd03f4/yarl-1.23.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5ec2f42d41ccbd5df0270d7df31618a8ee267bfa50997f5d720ddba86c4a83a6", size = 103008, upload-time = "2026-03-01T22:04:35.856Z" }, { url = "https://files.pythonhosted.org/packages/38/0f/0b4e3edcec794a86b853b0c6396c0a888d72dfce19b2d88c02ac289fb6c1/yarl-1.23.0-cp310-cp310-win32.whl", hash = "sha256:debe9c4f41c32990771be5c22b56f810659f9ddf3d63f67abfdcaa2c6c9c5c1d", size = 83073, upload-time = "2026-03-01T22:04:38.268Z" }, { url = "https://files.pythonhosted.org/packages/a0/71/ad95c33da18897e4c636528bbc24a1dd23fe16797de8bc4ec667b8db0ba4/yarl-1.23.0-cp310-cp310-win_amd64.whl", hash = "sha256:ab5f043cb8a2d71c981c09c510da013bc79fd661f5c60139f00dd3c3cc4f2ffb", size = 87328, upload-time = "2026-03-01T22:04:39.558Z" }, { url = "https://files.pythonhosted.org/packages/e2/14/dfa369523c79bccf9c9c746b0a63eb31f65db9418ac01275f7950962e504/yarl-1.23.0-cp310-cp310-win_arm64.whl", hash = "sha256:263cd4f47159c09b8b685890af949195b51d1aa82ba451c5847ca9bc6413c220", size = 82463, upload-time = "2026-03-01T22:04:41.454Z" }, { url = "https://files.pythonhosted.org/packages/a2/aa/60da938b8f0997ba3a911263c40d82b6f645a67902a490b46f3355e10fae/yarl-1.23.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b35d13d549077713e4414f927cdc388d62e543987c572baee613bf82f11a4b99", size = 123641, upload-time = "2026-03-01T22:04:42.841Z" }, { url = "https://files.pythonhosted.org/packages/24/84/e237607faf4e099dbb8a4f511cfd5efcb5f75918baad200ff7380635631b/yarl-1.23.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbb0fef01f0c6b38cb0f39b1f78fc90b807e0e3c86a7ff3ce74ad77ce5c7880c", size = 86248, upload-time = "2026-03-01T22:04:44.757Z" }, { url = "https://files.pythonhosted.org/packages/b2/0d/71ceabc14c146ba8ee3804ca7b3d42b1664c8440439de5214d366fec7d3a/yarl-1.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc52310451fc7c629e13c4e061cbe2dd01684d91f2f8ee2821b083c58bd72432", size = 85988, upload-time = "2026-03-01T22:04:46.365Z" }, { url = "https://files.pythonhosted.org/packages/8c/6c/4a90d59c572e46b270ca132aca66954f1175abd691f74c1ef4c6711828e2/yarl-1.23.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2c6b50c7b0464165472b56b42d4c76a7b864597007d9c085e8b63e185cf4a7a", size = 100566, upload-time = "2026-03-01T22:04:47.639Z" }, { url = "https://files.pythonhosted.org/packages/49/fb/c438fb5108047e629f6282a371e6e91cf3f97ee087c4fb748a1f32ceef55/yarl-1.23.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:aafe5dcfda86c8af00386d7781d4c2181b5011b7be3f2add5e99899ea925df05", size = 92079, upload-time = "2026-03-01T22:04:48.925Z" }, { url = "https://files.pythonhosted.org/packages/d9/13/d269aa1aed3e4f50a5a103f96327210cc5fa5dd2d50882778f13c7a14606/yarl-1.23.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9ee33b875f0b390564c1fb7bc528abf18c8ee6073b201c6ae8524aca778e2d83", size = 108741, upload-time = "2026-03-01T22:04:50.838Z" }, { url = "https://files.pythonhosted.org/packages/85/fb/115b16f22c37ea4437d323e472945bea97301c8ec6089868fa560abab590/yarl-1.23.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4c41e021bc6d7affb3364dc1e1e5fa9582b470f283748784bd6ea0558f87f42c", size = 108099, upload-time = "2026-03-01T22:04:52.499Z" }, { url = "https://files.pythonhosted.org/packages/9a/64/c53487d9f4968045b8afa51aed7ca44f58b2589e772f32745f3744476c82/yarl-1.23.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:99c8a9ed30f4164bc4c14b37a90208836cbf50d4ce2a57c71d0f52c7fb4f7598", size = 102678, upload-time = "2026-03-01T22:04:55.176Z" }, { url = "https://files.pythonhosted.org/packages/85/59/cd98e556fbb2bf8fab29c1a722f67ad45c5f3447cac798ab85620d1e70af/yarl-1.23.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f2af5c81a1f124609d5f33507082fc3f739959d4719b56877ab1ee7e7b3d602b", size = 100803, upload-time = "2026-03-01T22:04:56.588Z" }, { url = "https://files.pythonhosted.org/packages/9e/c0/b39770b56d4a9f0bb5f77e2f1763cd2d75cc2f6c0131e3b4c360348fcd65/yarl-1.23.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6b41389c19b07c760c7e427a3462e8ab83c4bb087d127f0e854c706ce1b9215c", size = 100163, upload-time = "2026-03-01T22:04:58.492Z" }, { url = "https://files.pythonhosted.org/packages/e7/64/6980f99ab00e1f0ff67cb84766c93d595b067eed07439cfccfc8fb28c1a6/yarl-1.23.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:1dc702e42d0684f42d6519c8d581e49c96cefaaab16691f03566d30658ee8788", size = 93859, upload-time = "2026-03-01T22:05:00.268Z" }, { url = "https://files.pythonhosted.org/packages/38/69/912e6c5e146793e5d4b5fe39ff5b00f4d22463dfd5a162bec565ac757673/yarl-1.23.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0e40111274f340d32ebcc0a5668d54d2b552a6cca84c9475859d364b380e3222", size = 108202, upload-time = "2026-03-01T22:05:02.273Z" }, { url = "https://files.pythonhosted.org/packages/59/97/35ca6767524687ad64e5f5c31ad54bc76d585585a9fcb40f649e7e82ffed/yarl-1.23.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:4764a6a7588561a9aef92f65bda2c4fb58fe7c675c0883862e6df97559de0bfb", size = 99866, upload-time = "2026-03-01T22:05:03.597Z" }, { url = "https://files.pythonhosted.org/packages/d3/1c/1a3387ee6d73589f6f2a220ae06f2984f6c20b40c734989b0a44f5987308/yarl-1.23.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:03214408cfa590df47728b84c679ae4ef00be2428e11630277be0727eba2d7cc", size = 107852, upload-time = "2026-03-01T22:05:04.986Z" }, { url = "https://files.pythonhosted.org/packages/a4/b8/35c0750fcd5a3f781058bfd954515dd4b1eab45e218cbb85cf11132215f1/yarl-1.23.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:170e26584b060879e29fac213e4228ef063f39128723807a312e5c7fec28eff2", size = 102919, upload-time = "2026-03-01T22:05:06.397Z" }, { url = "https://files.pythonhosted.org/packages/e5/1c/9a1979aec4a81896d597bcb2177827f2dbee3f5b7cc48b2d0dadb644b41d/yarl-1.23.0-cp311-cp311-win32.whl", hash = "sha256:51430653db848d258336cfa0244427b17d12db63d42603a55f0d4546f50f25b5", size = 82602, upload-time = "2026-03-01T22:05:08.444Z" }, { url = "https://files.pythonhosted.org/packages/93/22/b85eca6fa2ad9491af48c973e4c8cf6b103a73dbb271fe3346949449fca0/yarl-1.23.0-cp311-cp311-win_amd64.whl", hash = "sha256:bf49a3ae946a87083ef3a34c8f677ae4243f5b824bfc4c69672e72b3d6719d46", size = 87461, upload-time = "2026-03-01T22:05:10.145Z" }, { url = "https://files.pythonhosted.org/packages/93/95/07e3553fe6f113e6864a20bdc53a78113cda3b9ced8784ee52a52c9f80d8/yarl-1.23.0-cp311-cp311-win_arm64.whl", hash = "sha256:b39cb32a6582750b6cc77bfb3c49c0f8760dc18dc96ec9fb55fbb0f04e08b928", size = 82336, upload-time = "2026-03-01T22:05:11.554Z" }, { url = "https://files.pythonhosted.org/packages/88/8a/94615bc31022f711add374097ad4144d569e95ff3c38d39215d07ac153a0/yarl-1.23.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1932b6b8bba8d0160a9d1078aae5838a66039e8832d41d2992daa9a3a08f7860", size = 124737, upload-time = "2026-03-01T22:05:12.897Z" }, { url = "https://files.pythonhosted.org/packages/e3/6f/c6554045d59d64052698add01226bc867b52fe4a12373415d7991fdca95d/yarl-1.23.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:411225bae281f114067578891bc75534cfb3d92a3b4dfef7a6ca78ba354e6069", size = 87029, upload-time = "2026-03-01T22:05:14.376Z" }, { url = "https://files.pythonhosted.org/packages/19/2a/725ecc166d53438bc88f76822ed4b1e3b10756e790bafd7b523fe97c322d/yarl-1.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:13a563739ae600a631c36ce096615fe307f131344588b0bc0daec108cdb47b25", size = 86310, upload-time = "2026-03-01T22:05:15.71Z" }, { url = "https://files.pythonhosted.org/packages/99/30/58260ed98e6ff7f90ba84442c1ddd758c9170d70327394a6227b310cd60f/yarl-1.23.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cbf44c5cb4a7633d078788e1b56387e3d3cf2b8139a3be38040b22d6c3221c8", size = 97587, upload-time = "2026-03-01T22:05:17.384Z" }, { url = "https://files.pythonhosted.org/packages/76/0a/8b08aac08b50682e65759f7f8dde98ae8168f72487e7357a5d684c581ef9/yarl-1.23.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53ad387048f6f09a8969631e4de3f1bf70c50e93545d64af4f751b2498755072", size = 92528, upload-time = "2026-03-01T22:05:18.804Z" }, { url = "https://files.pythonhosted.org/packages/52/07/0b7179101fe5f8385ec6c6bb5d0cb9f76bd9fb4a769591ab6fb5cdbfc69a/yarl-1.23.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4a59ba56f340334766f3a4442e0efd0af895fae9e2b204741ef885c446b3a1a8", size = 105339, upload-time = "2026-03-01T22:05:20.235Z" }, { url = "https://files.pythonhosted.org/packages/d3/8a/36d82869ab5ec829ca8574dfcb92b51286fcfb1e9c7a73659616362dc880/yarl-1.23.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:803a3c3ce4acc62eaf01eaca1208dcf0783025ef27572c3336502b9c232005e7", size = 105061, upload-time = "2026-03-01T22:05:22.268Z" }, { url = "https://files.pythonhosted.org/packages/66/3e/868e5c3364b6cee19ff3e1a122194fa4ce51def02c61023970442162859e/yarl-1.23.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3d2bff8f37f8d0f96c7ec554d16945050d54462d6e95414babaa18bfafc7f51", size = 100132, upload-time = "2026-03-01T22:05:23.638Z" }, { url = "https://files.pythonhosted.org/packages/cf/26/9c89acf82f08a52cb52d6d39454f8d18af15f9d386a23795389d1d423823/yarl-1.23.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c75eb09e8d55bceb4367e83496ff8ef2bc7ea6960efb38e978e8073ea59ecb67", size = 99289, upload-time = "2026-03-01T22:05:25.749Z" }, { url = "https://files.pythonhosted.org/packages/6f/54/5b0db00d2cb056922356104468019c0a132e89c8d3ab67d8ede9f4483d2a/yarl-1.23.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877b0738624280e34c55680d6054a307aa94f7d52fa0e3034a9cc6e790871da7", size = 96950, upload-time = "2026-03-01T22:05:27.318Z" }, { url = "https://files.pythonhosted.org/packages/f6/40/10fa93811fd439341fad7e0718a86aca0de9548023bbb403668d6555acab/yarl-1.23.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b5405bb8f0e783a988172993cfc627e4d9d00432d6bbac65a923041edacf997d", size = 93960, upload-time = "2026-03-01T22:05:28.738Z" }, { url = "https://files.pythonhosted.org/packages/bc/d2/8ae2e6cd77d0805f4526e30ec43b6f9a3dfc542d401ac4990d178e4bf0cf/yarl-1.23.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c3a3598a832590c5a3ce56ab5576361b5688c12cb1d39429cf5dba30b510760", size = 104703, upload-time = "2026-03-01T22:05:30.438Z" }, { url = "https://files.pythonhosted.org/packages/2f/0c/b3ceacf82c3fe21183ce35fa2acf5320af003d52bc1fcf5915077681142e/yarl-1.23.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:8419ebd326430d1cbb7efb5292330a2cf39114e82df5cc3d83c9a0d5ebeaf2f2", size = 98325, upload-time = "2026-03-01T22:05:31.835Z" }, { url = "https://files.pythonhosted.org/packages/9d/e0/12900edd28bdab91a69bd2554b85ad7b151f64e8b521fe16f9ad2f56477a/yarl-1.23.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:be61f6fff406ca40e3b1d84716fde398fc08bc63dd96d15f3a14230a0973ed86", size = 105067, upload-time = "2026-03-01T22:05:33.358Z" }, { url = "https://files.pythonhosted.org/packages/15/61/74bb1182cf79c9bbe4eb6b1f14a57a22d7a0be5e9cedf8e2d5c2086474c3/yarl-1.23.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ceb13c5c858d01321b5d9bb65e4cf37a92169ea470b70fec6f236b2c9dd7e34", size = 100285, upload-time = "2026-03-01T22:05:35.4Z" }, { url = "https://files.pythonhosted.org/packages/69/7f/cd5ef733f2550de6241bd8bd8c3febc78158b9d75f197d9c7baa113436af/yarl-1.23.0-cp312-cp312-win32.whl", hash = "sha256:fffc45637bcd6538de8b85f51e3df3223e4ad89bccbfca0481c08c7fc8b7ed7d", size = 82359, upload-time = "2026-03-01T22:05:36.811Z" }, { url = "https://files.pythonhosted.org/packages/f5/be/25216a49daeeb7af2bec0db22d5e7df08ed1d7c9f65d78b14f3b74fd72fc/yarl-1.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:f69f57305656a4852f2a7203efc661d8c042e6cc67f7acd97d8667fb448a426e", size = 87674, upload-time = "2026-03-01T22:05:38.171Z" }, { url = "https://files.pythonhosted.org/packages/d2/35/aeab955d6c425b227d5b7247eafb24f2653fedc32f95373a001af5dfeb9e/yarl-1.23.0-cp312-cp312-win_arm64.whl", hash = "sha256:6e87a6e8735b44816e7db0b2fbc9686932df473c826b0d9743148432e10bb9b9", size = 81879, upload-time = "2026-03-01T22:05:40.006Z" }, { url = "https://files.pythonhosted.org/packages/9a/4b/a0a6e5d0ee8a2f3a373ddef8a4097d74ac901ac363eea1440464ccbe0898/yarl-1.23.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:16c6994ac35c3e74fb0ae93323bf8b9c2a9088d55946109489667c510a7d010e", size = 123796, upload-time = "2026-03-01T22:05:41.412Z" }, { url = "https://files.pythonhosted.org/packages/67/b6/8925d68af039b835ae876db5838e82e76ec87b9782ecc97e192b809c4831/yarl-1.23.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4a42e651629dafb64fd5b0286a3580613702b5809ad3f24934ea87595804f2c5", size = 86547, upload-time = "2026-03-01T22:05:42.841Z" }, { url = "https://files.pythonhosted.org/packages/ae/50/06d511cc4b8e0360d3c94af051a768e84b755c5eb031b12adaaab6dec6e5/yarl-1.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7c6b9461a2a8b47c65eef63bb1c76a4f1c119618ffa99ea79bc5bb1e46c5821b", size = 85854, upload-time = "2026-03-01T22:05:44.85Z" }, { url = "https://files.pythonhosted.org/packages/c4/f4/4e30b250927ffdab4db70da08b9b8d2194d7c7b400167b8fbeca1e4701ca/yarl-1.23.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2569b67d616eab450d262ca7cb9f9e19d2f718c70a8b88712859359d0ab17035", size = 98351, upload-time = "2026-03-01T22:05:46.836Z" }, { url = "https://files.pythonhosted.org/packages/86/fc/4118c5671ea948208bdb1492d8b76bdf1453d3e73df051f939f563e7dcc5/yarl-1.23.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e9d9a4d06d3481eab79803beb4d9bd6f6a8e781ec078ac70d7ef2dcc29d1bea5", size = 92711, upload-time = "2026-03-01T22:05:48.316Z" }, { url = "https://files.pythonhosted.org/packages/56/11/1ed91d42bd9e73c13dc9e7eb0dd92298d75e7ac4dd7f046ad0c472e231cd/yarl-1.23.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f514f6474e04179d3d33175ed3f3e31434d3130d42ec153540d5b157deefd735", size = 106014, upload-time = "2026-03-01T22:05:50.028Z" }, { url = "https://files.pythonhosted.org/packages/ce/c9/74e44e056a23fbc33aca71779ef450ca648a5bc472bdad7a82339918f818/yarl-1.23.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fda207c815b253e34f7e1909840fd14299567b1c0eb4908f8c2ce01a41265401", size = 105557, upload-time = "2026-03-01T22:05:51.416Z" }, { url = "https://files.pythonhosted.org/packages/66/fe/b1e10b08d287f518994f1e2ff9b6d26f0adeecd8dd7d533b01bab29a3eda/yarl-1.23.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34b6cf500e61c90f305094911f9acc9c86da1a05a7a3f5be9f68817043f486e4", size = 101559, upload-time = "2026-03-01T22:05:52.872Z" }, { url = "https://files.pythonhosted.org/packages/72/59/c5b8d94b14e3d3c2a9c20cb100119fd534ab5a14b93673ab4cc4a4141ea5/yarl-1.23.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d7504f2b476d21653e4d143f44a175f7f751cd41233525312696c76aa3dbb23f", size = 100502, upload-time = "2026-03-01T22:05:54.954Z" }, { url = "https://files.pythonhosted.org/packages/77/4f/96976cb54cbfc5c9fd73ed4c51804f92f209481d1fb190981c0f8a07a1d7/yarl-1.23.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:578110dd426f0d209d1509244e6d4a3f1a3e9077655d98c5f22583d63252a08a", size = 98027, upload-time = "2026-03-01T22:05:56.409Z" }, { url = "https://files.pythonhosted.org/packages/63/6e/904c4f476471afdbad6b7e5b70362fb5810e35cd7466529a97322b6f5556/yarl-1.23.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:609d3614d78d74ebe35f54953c5bbd2ac647a7ddb9c30a5d877580f5e86b22f2", size = 95369, upload-time = "2026-03-01T22:05:58.141Z" }, { url = "https://files.pythonhosted.org/packages/9d/40/acfcdb3b5f9d68ef499e39e04d25e141fe90661f9d54114556cf83be8353/yarl-1.23.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4966242ec68afc74c122f8459abd597afd7d8a60dc93d695c1334c5fd25f762f", size = 105565, upload-time = "2026-03-01T22:06:00.286Z" }, { url = "https://files.pythonhosted.org/packages/5e/c6/31e28f3a6ba2869c43d124f37ea5260cac9c9281df803c354b31f4dd1f3c/yarl-1.23.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e0fd068364a6759bc794459f0a735ab151d11304346332489c7972bacbe9e72b", size = 99813, upload-time = "2026-03-01T22:06:01.712Z" }, { url = "https://files.pythonhosted.org/packages/08/1f/6f65f59e72d54aa467119b63fc0b0b1762eff0232db1f4720cd89e2f4a17/yarl-1.23.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:39004f0ad156da43e86aa71f44e033de68a44e5a31fc53507b36dd253970054a", size = 105632, upload-time = "2026-03-01T22:06:03.188Z" }, { url = "https://files.pythonhosted.org/packages/a3/c4/18b178a69935f9e7a338127d5b77d868fdc0f0e49becd286d51b3a18c61d/yarl-1.23.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e5723c01a56c5028c807c701aa66722916d2747ad737a046853f6c46f4875543", size = 101895, upload-time = "2026-03-01T22:06:04.651Z" }, { url = "https://files.pythonhosted.org/packages/8f/54/f5b870b5505663911dba950a8e4776a0dbd51c9c54c0ae88e823e4b874a0/yarl-1.23.0-cp313-cp313-win32.whl", hash = "sha256:1b6b572edd95b4fa8df75de10b04bc81acc87c1c7d16bcdd2035b09d30acc957", size = 82356, upload-time = "2026-03-01T22:06:06.04Z" }, { url = "https://files.pythonhosted.org/packages/7a/84/266e8da36879c6edcd37b02b547e2d9ecdfea776be49598e75696e3316e1/yarl-1.23.0-cp313-cp313-win_amd64.whl", hash = "sha256:baaf55442359053c7d62f6f8413a62adba3205119bcb6f49594894d8be47e5e3", size = 87515, upload-time = "2026-03-01T22:06:08.107Z" }, { url = "https://files.pythonhosted.org/packages/00/fd/7e1c66efad35e1649114fa13f17485f62881ad58edeeb7f49f8c5e748bf9/yarl-1.23.0-cp313-cp313-win_arm64.whl", hash = "sha256:fb4948814a2a98e3912505f09c9e7493b1506226afb1f881825368d6fb776ee3", size = 81785, upload-time = "2026-03-01T22:06:10.181Z" }, { url = "https://files.pythonhosted.org/packages/9c/fc/119dd07004f17ea43bb91e3ece6587759edd7519d6b086d16bfbd3319982/yarl-1.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:aecfed0b41aa72b7881712c65cf764e39ce2ec352324f5e0837c7048d9e6daaa", size = 130719, upload-time = "2026-03-01T22:06:11.708Z" }, { url = "https://files.pythonhosted.org/packages/e6/0d/9f2348502fbb3af409e8f47730282cd6bc80dec6630c1e06374d882d6eb2/yarl-1.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a41bcf68efd19073376eb8cf948b8d9be0af26256403e512bb18f3966f1f9120", size = 89690, upload-time = "2026-03-01T22:06:13.429Z" }, { url = "https://files.pythonhosted.org/packages/50/93/e88f3c80971b42cfc83f50a51b9d165a1dbf154b97005f2994a79f212a07/yarl-1.23.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cde9a2ecd91668bcb7f077c4966d8ceddb60af01b52e6e3e2680e4cf00ad1a59", size = 89851, upload-time = "2026-03-01T22:06:15.53Z" }, { url = "https://files.pythonhosted.org/packages/1c/07/61c9dd8ba8f86473263b4036f70fb594c09e99c0d9737a799dfd8bc85651/yarl-1.23.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5023346c4ee7992febc0068e7593de5fa2bf611848c08404b35ebbb76b1b0512", size = 95874, upload-time = "2026-03-01T22:06:17.553Z" }, { url = "https://files.pythonhosted.org/packages/9e/e9/f9ff8ceefba599eac6abddcfb0b3bee9b9e636e96dbf54342a8577252379/yarl-1.23.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d1009abedb49ae95b136a8904a3f71b342f849ffeced2d3747bf29caeda218c4", size = 88710, upload-time = "2026-03-01T22:06:19.004Z" }, { url = "https://files.pythonhosted.org/packages/eb/78/0231bfcc5d4c8eec220bc2f9ef82cb4566192ea867a7c5b4148f44f6cbcd/yarl-1.23.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a8d00f29b42f534cc8aa3931cfe773b13b23e561e10d2b26f27a8d309b0e82a1", size = 101033, upload-time = "2026-03-01T22:06:21.203Z" }, { url = "https://files.pythonhosted.org/packages/cd/9b/30ea5239a61786f18fd25797151a17fbb3be176977187a48d541b5447dd4/yarl-1.23.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:95451e6ce06c3e104556d73b559f5da6c34a069b6b62946d3ad66afcd51642ea", size = 100817, upload-time = "2026-03-01T22:06:22.738Z" }, { url = "https://files.pythonhosted.org/packages/62/e2/a4980481071791bc83bce2b7a1a1f7adcabfa366007518b4b845e92eeee3/yarl-1.23.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:531ef597132086b6cf96faa7c6c1dcd0361dd5f1694e5cc30375907b9b7d3ea9", size = 97482, upload-time = "2026-03-01T22:06:24.21Z" }, { url = "https://files.pythonhosted.org/packages/e5/1e/304a00cf5f6100414c4b5a01fc7ff9ee724b62158a08df2f8170dfc72a2d/yarl-1.23.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:88f9fb0116fbfcefcab70f85cf4b74a2b6ce5d199c41345296f49d974ddb4123", size = 95949, upload-time = "2026-03-01T22:06:25.697Z" }, { url = "https://files.pythonhosted.org/packages/68/03/093f4055ed4cae649ac53bca3d180bd37102e9e11d048588e9ab0c0108d0/yarl-1.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e7b0460976dc75cb87ad9cc1f9899a4b97751e7d4e77ab840fc9b6d377b8fd24", size = 95839, upload-time = "2026-03-01T22:06:27.309Z" }, { url = "https://files.pythonhosted.org/packages/b9/28/4c75ebb108f322aa8f917ae10a8ffa4f07cae10a8a627b64e578617df6a0/yarl-1.23.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:115136c4a426f9da976187d238e84139ff6b51a20839aa6e3720cd1026d768de", size = 90696, upload-time = "2026-03-01T22:06:29.048Z" }, { url = "https://files.pythonhosted.org/packages/23/9c/42c2e2dd91c1a570402f51bdf066bfdb1241c2240ba001967bad778e77b7/yarl-1.23.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:ead11956716a940c1abc816b7df3fa2b84d06eaed8832ca32f5c5e058c65506b", size = 100865, upload-time = "2026-03-01T22:06:30.525Z" }, { url = "https://files.pythonhosted.org/packages/74/05/1bcd60a8a0a914d462c305137246b6f9d167628d73568505fce3f1cb2e65/yarl-1.23.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:fe8f8f5e70e6dbdfca9882cd9deaac058729bcf323cf7a58660901e55c9c94f6", size = 96234, upload-time = "2026-03-01T22:06:32.692Z" }, { url = "https://files.pythonhosted.org/packages/90/b2/f52381aac396d6778ce516b7bc149c79e65bfc068b5de2857ab69eeea3b7/yarl-1.23.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:a0e317df055958a0c1e79e5d2aa5a5eaa4a6d05a20d4b0c9c3f48918139c9fc6", size = 100295, upload-time = "2026-03-01T22:06:34.268Z" }, { url = "https://files.pythonhosted.org/packages/e5/e8/638bae5bbf1113a659b2435d8895474598afe38b4a837103764f603aba56/yarl-1.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f0fd84de0c957b2d280143522c4f91a73aada1923caee763e24a2b3fda9f8a5", size = 97784, upload-time = "2026-03-01T22:06:35.864Z" }, { url = "https://files.pythonhosted.org/packages/80/25/a3892b46182c586c202629fc2159aa13975d3741d52ebd7347fd501d48d5/yarl-1.23.0-cp313-cp313t-win32.whl", hash = "sha256:93a784271881035ab4406a172edb0faecb6e7d00f4b53dc2f55919d6c9688595", size = 88313, upload-time = "2026-03-01T22:06:37.39Z" }, { url = "https://files.pythonhosted.org/packages/43/68/8c5b36aa5178900b37387937bc2c2fe0e9505537f713495472dcf6f6fccc/yarl-1.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:dd00607bffbf30250fe108065f07453ec124dbf223420f57f5e749b04295e090", size = 94932, upload-time = "2026-03-01T22:06:39.579Z" }, { url = "https://files.pythonhosted.org/packages/c6/cc/d79ba8292f51f81f4dc533a8ccfb9fc6992cabf0998ed3245de7589dc07c/yarl-1.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:ac09d42f48f80c9ee1635b2fcaa819496a44502737660d3c0f2ade7526d29144", size = 84786, upload-time = "2026-03-01T22:06:41.988Z" }, { url = "https://files.pythonhosted.org/packages/90/98/b85a038d65d1b92c3903ab89444f48d3cee490a883477b716d7a24b1a78c/yarl-1.23.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:21d1b7305a71a15b4794b5ff22e8eef96ff4a6d7f9657155e5aa419444b28912", size = 124455, upload-time = "2026-03-01T22:06:43.615Z" }, { url = "https://files.pythonhosted.org/packages/39/54/bc2b45559f86543d163b6e294417a107bb87557609007c007ad889afec18/yarl-1.23.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:85610b4f27f69984932a7abbe52703688de3724d9f72bceb1cca667deff27474", size = 86752, upload-time = "2026-03-01T22:06:45.425Z" }, { url = "https://files.pythonhosted.org/packages/24/f9/e8242b68362bffe6fb536c8db5076861466fc780f0f1b479fc4ffbebb128/yarl-1.23.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23f371bd662cf44a7630d4d113101eafc0cfa7518a2760d20760b26021454719", size = 86291, upload-time = "2026-03-01T22:06:46.974Z" }, { url = "https://files.pythonhosted.org/packages/ea/d8/d1cb2378c81dd729e98c716582b1ccb08357e8488e4c24714658cc6630e8/yarl-1.23.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a80f77dc1acaaa61f0934176fccca7096d9b1ff08c8ba9cddf5ae034a24319", size = 99026, upload-time = "2026-03-01T22:06:48.459Z" }, { url = "https://files.pythonhosted.org/packages/0a/ff/7196790538f31debe3341283b5b0707e7feb947620fc5e8236ef28d44f72/yarl-1.23.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:bd654fad46d8d9e823afbb4f87c79160b5a374ed1ff5bde24e542e6ba8f41434", size = 92355, upload-time = "2026-03-01T22:06:50.306Z" }, { url = "https://files.pythonhosted.org/packages/c1/56/25d58c3eddde825890a5fe6aa1866228377354a3c39262235234ab5f616b/yarl-1.23.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:682bae25f0a0dd23a056739f23a134db9f52a63e2afd6bfb37ddc76292bbd723", size = 106417, upload-time = "2026-03-01T22:06:52.1Z" }, { url = "https://files.pythonhosted.org/packages/51/8a/882c0e7bc8277eb895b31bce0138f51a1ba551fc2e1ec6753ffc1e7c1377/yarl-1.23.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a82836cab5f197a0514235aaf7ffccdc886ccdaa2324bc0aafdd4ae898103039", size = 106422, upload-time = "2026-03-01T22:06:54.424Z" }, { url = "https://files.pythonhosted.org/packages/42/2b/fef67d616931055bf3d6764885990a3ac647d68734a2d6a9e1d13de437a2/yarl-1.23.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c57676bdedc94cd3bc37724cf6f8cd2779f02f6aba48de45feca073e714fe52", size = 101915, upload-time = "2026-03-01T22:06:55.895Z" }, { url = "https://files.pythonhosted.org/packages/18/6a/530e16aebce27c5937920f3431c628a29a4b6b430fab3fd1c117b26ff3f6/yarl-1.23.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c7f8dc16c498ff06497c015642333219871effba93e4a2e8604a06264aca5c5c", size = 100690, upload-time = "2026-03-01T22:06:58.21Z" }, { url = "https://files.pythonhosted.org/packages/88/08/93749219179a45e27b036e03260fda05190b911de8e18225c294ac95bbc9/yarl-1.23.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5ee586fb17ff8f90c91cf73c6108a434b02d69925f44f5f8e0d7f2f260607eae", size = 98750, upload-time = "2026-03-01T22:06:59.794Z" }, { url = "https://files.pythonhosted.org/packages/d9/cf/ea424a004969f5d81a362110a6ac1496d79efdc6d50c2c4b2e3ea0fc2519/yarl-1.23.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:17235362f580149742739cc3828b80e24029d08cbb9c4bda0242c7b5bc610a8e", size = 94685, upload-time = "2026-03-01T22:07:01.375Z" }, { url = "https://files.pythonhosted.org/packages/e2/b7/14341481fe568e2b0408bcf1484c652accafe06a0ade9387b5d3fd9df446/yarl-1.23.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:0793e2bd0cf14234983bbb371591e6bea9e876ddf6896cdcc93450996b0b5c85", size = 106009, upload-time = "2026-03-01T22:07:03.151Z" }, { url = "https://files.pythonhosted.org/packages/0a/e6/5c744a9b54f4e8007ad35bce96fbc9218338e84812d36f3390cea616881a/yarl-1.23.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3650dc2480f94f7116c364096bc84b1d602f44224ef7d5c7208425915c0475dd", size = 100033, upload-time = "2026-03-01T22:07:04.701Z" }, { url = "https://files.pythonhosted.org/packages/0c/23/e3bfc188d0b400f025bc49d99793d02c9abe15752138dcc27e4eaf0c4a9e/yarl-1.23.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f40e782d49630ad384db66d4d8b73ff4f1b8955dc12e26b09a3e3af064b3b9d6", size = 106483, upload-time = "2026-03-01T22:07:06.231Z" }, { url = "https://files.pythonhosted.org/packages/72/42/f0505f949a90b3f8b7a363d6cbdf398f6e6c58946d85c6d3a3bc70595b26/yarl-1.23.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:94f8575fbdf81749008d980c17796097e645574a3b8c28ee313931068dad14fe", size = 102175, upload-time = "2026-03-01T22:07:08.4Z" }, { url = "https://files.pythonhosted.org/packages/aa/65/b39290f1d892a9dd671d1c722014ca062a9c35d60885d57e5375db0404b5/yarl-1.23.0-cp314-cp314-win32.whl", hash = "sha256:c8aa34a5c864db1087d911a0b902d60d203ea3607d91f615acd3f3108ac32169", size = 83871, upload-time = "2026-03-01T22:07:09.968Z" }, { url = "https://files.pythonhosted.org/packages/a9/5b/9b92f54c784c26e2a422e55a8d2607ab15b7ea3349e28359282f84f01d43/yarl-1.23.0-cp314-cp314-win_amd64.whl", hash = "sha256:63e92247f383c85ab00dd0091e8c3fa331a96e865459f5ee80353c70a4a42d70", size = 89093, upload-time = "2026-03-01T22:07:11.501Z" }, { url = "https://files.pythonhosted.org/packages/e0/7d/8a84dc9381fd4412d5e7ff04926f9865f6372b4c2fd91e10092e65d29eb8/yarl-1.23.0-cp314-cp314-win_arm64.whl", hash = "sha256:70efd20be968c76ece7baa8dafe04c5be06abc57f754d6f36f3741f7aa7a208e", size = 83384, upload-time = "2026-03-01T22:07:13.069Z" }, { url = "https://files.pythonhosted.org/packages/dd/8d/d2fad34b1c08aa161b74394183daa7d800141aaaee207317e82c790b418d/yarl-1.23.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:9a18d6f9359e45722c064c97464ec883eb0e0366d33eda61cb19a244bf222679", size = 131019, upload-time = "2026-03-01T22:07:14.903Z" }, { url = "https://files.pythonhosted.org/packages/19/ff/33009a39d3ccf4b94d7d7880dfe17fb5816c5a4fe0096d9b56abceea9ac7/yarl-1.23.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2803ed8b21ca47a43da80a6fd1ed3019d30061f7061daa35ac54f63933409412", size = 89894, upload-time = "2026-03-01T22:07:17.372Z" }, { url = "https://files.pythonhosted.org/packages/0c/f1/dab7ac5e7306fb79c0190766a3c00b4cb8d09a1f390ded68c85a5934faf5/yarl-1.23.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:394906945aa8b19fc14a61cf69743a868bb8c465efe85eee687109cc540b98f4", size = 89979, upload-time = "2026-03-01T22:07:19.361Z" }, { url = "https://files.pythonhosted.org/packages/aa/b1/08e95f3caee1fad6e65017b9f26c1d79877b502622d60e517de01e72f95d/yarl-1.23.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:71d006bee8397a4a89f469b8deb22469fe7508132d3c17fa6ed871e79832691c", size = 95943, upload-time = "2026-03-01T22:07:21.266Z" }, { url = "https://files.pythonhosted.org/packages/c0/cc/6409f9018864a6aa186c61175b977131f373f1988e198e031236916e87e4/yarl-1.23.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:62694e275c93d54f7ccedcfef57d42761b2aad5234b6be1f3e3026cae4001cd4", size = 88786, upload-time = "2026-03-01T22:07:23.129Z" }, { url = "https://files.pythonhosted.org/packages/76/40/cc22d1d7714b717fde2006fad2ced5efe5580606cb059ae42117542122f3/yarl-1.23.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a31de1613658308efdb21ada98cbc86a97c181aa050ba22a808120bb5be3ab94", size = 101307, upload-time = "2026-03-01T22:07:24.689Z" }, { url = "https://files.pythonhosted.org/packages/8f/0d/476c38e85ddb4c6ec6b20b815bdd779aa386a013f3d8b85516feee55c8dc/yarl-1.23.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb1e8b8d66c278b21d13b0a7ca22c41dd757a7c209c6b12c313e445c31dd3b28", size = 100904, upload-time = "2026-03-01T22:07:26.287Z" }, { url = "https://files.pythonhosted.org/packages/72/32/0abe4a76d59adf2081dcb0397168553ece4616ada1c54d1c49d8936c74f8/yarl-1.23.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50f9d8d531dfb767c565f348f33dd5139a6c43f5cbdf3f67da40d54241df93f6", size = 97728, upload-time = "2026-03-01T22:07:27.906Z" }, { url = "https://files.pythonhosted.org/packages/b7/35/7b30f4810fba112f60f5a43237545867504e15b1c7647a785fbaf588fac2/yarl-1.23.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:575aa4405a656e61a540f4a80eaa5260f2a38fff7bfdc4b5f611840d76e9e277", size = 95964, upload-time = "2026-03-01T22:07:30.198Z" }, { url = "https://files.pythonhosted.org/packages/2d/86/ed7a73ab85ef00e8bb70b0cb5421d8a2a625b81a333941a469a6f4022828/yarl-1.23.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:041b1a4cefacf65840b4e295c6985f334ba83c30607441ae3cf206a0eed1a2e4", size = 95882, upload-time = "2026-03-01T22:07:32.132Z" }, { url = "https://files.pythonhosted.org/packages/19/90/d56967f61a29d8498efb7afb651e0b2b422a1e9b47b0ab5f4e40a19b699b/yarl-1.23.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:d38c1e8231722c4ce40d7593f28d92b5fc72f3e9774fe73d7e800ec32299f63a", size = 90797, upload-time = "2026-03-01T22:07:34.404Z" }, { url = "https://files.pythonhosted.org/packages/72/00/8b8f76909259f56647adb1011d7ed8b321bcf97e464515c65016a47ecdf0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:d53834e23c015ee83a99377db6e5e37d8484f333edb03bd15b4bc312cc7254fb", size = 101023, upload-time = "2026-03-01T22:07:35.953Z" }, { url = "https://files.pythonhosted.org/packages/ac/e2/cab11b126fb7d440281b7df8e9ddbe4851e70a4dde47a202b6642586b8d9/yarl-1.23.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:2e27c8841126e017dd2a054a95771569e6070b9ee1b133366d8b31beb5018a41", size = 96227, upload-time = "2026-03-01T22:07:37.594Z" }, { url = "https://files.pythonhosted.org/packages/c2/9b/2c893e16bfc50e6b2edf76c1a9eb6cb0c744346197e74c65e99ad8d634d0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:76855800ac56f878847a09ce6dba727c93ca2d89c9e9d63002d26b916810b0a2", size = 100302, upload-time = "2026-03-01T22:07:39.334Z" }, { url = "https://files.pythonhosted.org/packages/28/ec/5498c4e3a6d5f1003beb23405671c2eb9cdbf3067d1c80f15eeafe301010/yarl-1.23.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e09fd068c2e169a7070d83d3bde728a4d48de0549f975290be3c108c02e499b4", size = 98202, upload-time = "2026-03-01T22:07:41.717Z" }, { url = "https://files.pythonhosted.org/packages/fe/c3/cd737e2d45e70717907f83e146f6949f20cc23cd4bf7b2688727763aa458/yarl-1.23.0-cp314-cp314t-win32.whl", hash = "sha256:73309162a6a571d4cbd3b6a1dcc703c7311843ae0d1578df6f09be4e98df38d4", size = 90558, upload-time = "2026-03-01T22:07:43.433Z" }, { url = "https://files.pythonhosted.org/packages/e1/19/3774d162f6732d1cfb0b47b4140a942a35ca82bb19b6db1f80e9e7bdc8f8/yarl-1.23.0-cp314-cp314t-win_amd64.whl", hash = "sha256:4503053d296bc6e4cbd1fad61cf3b6e33b939886c4f249ba7c78b602214fabe2", size = 97610, upload-time = "2026-03-01T22:07:45.773Z" }, { url = "https://files.pythonhosted.org/packages/51/47/3fa2286c3cb162c71cdb34c4224d5745a1ceceb391b2bd9b19b668a8d724/yarl-1.23.0-cp314-cp314t-win_arm64.whl", hash = "sha256:44bb7bef4ea409384e3f8bc36c063d77ea1b8d4a5b2706956c0d6695f07dcc25", size = 86041, upload-time = "2026-03-01T22:07:49.026Z" }, { url = "https://files.pythonhosted.org/packages/69/68/c8739671f5699c7dc470580a4f821ef37c32c4cb0b047ce223a7f115757f/yarl-1.23.0-py3-none-any.whl", hash = "sha256:a2df6afe50dea8ae15fa34c9f824a3ee958d785fd5d089063d960bae1daa0a3f", size = 48288, upload-time = "2026-03-01T22:07:51.388Z" }, ] [[package]] name = "zipp" version = "3.23.0" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, ]