diff --git a/.github/workflows/ai-workspace-cli-e2e.yml b/.github/workflows/ai-workspace-cli-e2e.yml new file mode 100644 index 000000000..a2ef79195 --- /dev/null +++ b/.github/workflows/ai-workspace-cli-e2e.yml @@ -0,0 +1,54 @@ +name: AI Workspace CLI E2E (daily) + +# Daily end-to-end check that the `ap` CLI's AI Workspace behaviour persists +# against a real platform-api backend. It builds the platform-api image and the +# CLI from source, boots platform-api (SQLite) as the AI Workspace backend, then +# drives the CLI: register the workspace, `ap project init` the three artifact +# kinds (LLM provider, LLM proxy, MCP proxy), and `ap ai-workspace build`/`apply` +# each — asserting create, read-back, and update all persist. +# +# This is intentionally NOT on the per-PR critical path (it builds the +# platform-api image and CLI). It runs once a day and on demand. +on: + schedule: + - cron: '0 3 * * *' # 03:00 UTC daily + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: true + +jobs: + ai-workspace-cli-e2e: + runs-on: ubuntu-24.04 + steps: + - name: Checkout code + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + persist-credentials: false + + - name: Set up Go + uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5.6.0 + with: + go-version: '1.26.5' + cache-dependency-path: '**/go.sum' + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 + + - name: Build platform-api image + run: make -C platform-api build IMAGE_NAME=platform-api VERSION=it-e2e + + - name: Build ap CLI binary + run: make -C cli/src build-skip-tests + + - name: Run AI Workspace CLI e2e + # PLATFORM_API_IMAGE pins the compose to the image built above; the suite + # boots it, logs in, and drives the CLI built at cli/src/build/ap. + env: + PLATFORM_API_IMAGE: platform-api:it-e2e + run: go test -run TestFeatures -count=1 -v -timeout 20m ./... + working-directory: tests/ai-workspace-cli-e2e diff --git a/go.work b/go.work index 93e223860..fb3ae70c9 100644 --- a/go.work +++ b/go.work @@ -23,6 +23,7 @@ use ( ./samples/sample-service ./sdk/ai ./sdk/core + ./tests/ai-workspace-cli-e2e ./tests/integration-e2e ./tests/mock-servers/mock-platform-api ) diff --git a/tests/ai-workspace-cli-e2e/README.md b/tests/ai-workspace-cli-e2e/README.md new file mode 100644 index 000000000..9ba96104d --- /dev/null +++ b/tests/ai-workspace-cli-e2e/README.md @@ -0,0 +1,83 @@ +# AI Workspace CLI end-to-end tests + +A godog (BDD) suite that verifies the `ap` CLI's **AI Workspace** behaviour +persists against a real `platform-api` backend. It exercises the documented +end-to-end flow from [`docs/cli/end-to-end-workflow.md`](../../docs/cli/end-to-end-workflow.md) +and [`docs/cli/ai-workspace/README.md`](../../docs/cli/ai-workspace/README.md): + +1. Boot `platform-api` (SQLite, standalone) as the AI Workspace backend. +2. Log in as `admin`/`admin` at `/api/portal/v0.9/auth/login` for a bearer token. +3. Create a server-side project (`POST /api/v0.9/projects`) — its handle is the + `--project-id` for the project-scoped kinds — and register the gateways the + artifacts associate with (`POST /api/v0.9/gateways`, handles `prod-eu-01` / + `prod-eu-02`), since `associatedGateways` handles are validated server-side. +4. Register `platform-api` as an AI Workspace target in an isolated CLI config + (`ap ai-workspace add --auth oauth`); the token is supplied per call via + `WSO2AP_AIWORKSPACE_TOKEN`. +5. For each of the three artifact kinds — **LLM provider**, **LLM proxy**, + **MCP proxy** — `ap project init`, apply the `create` content, read it back + with both `ap ai-workspace get --id` and `ap ai-workspace + list`, then apply the `edit` content (which adds `spec.associatedGateways`) to + confirm the update path persists — verified by reading the associated gateway + back from the server. + +The artifact content comes from a real demo set (`create`/`edit` variants per +kind). The `edit` metadata adds `spec.associatedGateways`; the LLM proxy +references the LLM provider, so scenarios run provider → proxy → MCP. + +It runs **once a day** in CI (see +[`.github/workflows/ai-workspace-cli-e2e.yml`](../../.github/workflows/ai-workspace-cli-e2e.yml)), +not on every PR, because it builds the `platform-api` image and the CLI from +source. + +## Layout + +``` +tests/ai-workspace-cli-e2e/ +├── docker-compose.yaml # platform-api only (SQLite, standalone) +├── platform-api-config.toml # file-based auth (admin/admin) +├── suite_test.go # boot/login/create-project/register-workspace/teardown + CLI runner +├── steps_test.go # per-kind init/build/apply/get step definitions +├── features/ +│ └── ai-workspace-cli.feature +└── resources/ # demo artifacts copied over the scaffold + ├── llm-provider/ + │ ├── definition.yaml # shared OpenAPI spec (create + edit) + │ ├── create/{metadata,runtime}.yaml + │ └── edit/{metadata,runtime}.yaml # edit metadata adds spec.associatedGateways + ├── llm-proxy/ (same shape; runtime references the claude-provider) + └── mcp/ (same shape; definition.yaml is capabilities, not OpenAPI) +``` + +The `llm-proxy` artifact references the `claude-provider` created by the +`llm-provider` scenario, so scenarios run provider → proxy → MCP in order and +share the suite-scoped backend, token, project, and registered gateways. + +## Running locally + +Prerequisites: Docker running, Go 1.26.5. + +```shell +# 1. Build the platform-api image (tag must match PLATFORM_API_IMAGE below). +make -C platform-api build IMAGE_NAME=platform-api VERSION=it-e2e + +# 2. Build the ap CLI binary (lands at cli/src/build/ap). +make -C cli/src build-skip-tests + +# 3. Run the suite. +cd tests/ai-workspace-cli-e2e +PLATFORM_API_IMAGE=platform-api:it-e2e go test -run TestFeatures -count=1 -v -timeout 20m ./... +``` + +### Knobs + +| Variable | Default | Purpose | +| -------------------- | ----------------------- | --------------------------------------------------- | +| `PLATFORM_API_IMAGE` | `platform-api:it-e2e` | platform-api image the compose stack runs. | +| `AP_CLI_BINARY` | `../../cli/src/build/ap`| Path to the `ap` binary under test. | +| `PA_HOST_PORT` | `9243` | Host port platform-api is published on. | +| `AW_TAGS` | (all) | godog tag filter, e.g. `@mcp-proxy`. | +| `AW_KEEP` | (unset) | If set, the compose stack is left running for debug.| + +The CLI runs with an isolated `HOME`, so it never touches your real +`~/.wso2ap/config.yaml`. diff --git a/tests/ai-workspace-cli-e2e/docker-compose.yaml b/tests/ai-workspace-cli-e2e/docker-compose.yaml new file mode 100644 index 000000000..4ec7dbc7d --- /dev/null +++ b/tests/ai-workspace-cli-e2e/docker-compose.yaml @@ -0,0 +1,38 @@ +# Standalone platform-api stack for the AI Workspace CLI integration test. +# +# platform-api is the AI Workspace backend the `ap ai-workspace` commands target. +# The gateway data plane is NOT needed here: the test only exercises the CLI -> +# platform-api control-plane path (login, projects, llm-providers, llm-proxies, +# mcp-proxies), so a single platform-api process on SQLite is enough. +# +# The image tag is overridable with PLATFORM_API_IMAGE; the CI workflow builds +# platform-api:it-e2e from source and passes it through (same pattern the +# platform-api-devportal-e2e / platform-api-gateway-e2e suites use). +services: + platform-api: + image: ${PLATFORM_API_IMAGE:-platform-api:it-e2e} + command: ["./platform-api", "-config", "/etc/platform-api/config.toml"] + environment: + - DATABASE_DRIVER=sqlite3 + - DATABASE_PATH=/app/data/platform.db + - DATABASE_EXECUTE_SCHEMA_DDL=true + # Single encryption key — must be 32 bytes (64 hex chars). Used for all + # at-rest encryption (secrets, subscription tokens, HMAC). + - ENCRYPTION_KEY=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef + - AUTH_FILE_BASED_ENABLED=true + - AUTH_FILE_BASED_ORGANIZATION_ID=default + - AUTH_FILE_BASED_ORGANIZATION_UUID=99089a17-72e0-4dd8-a2f4-c8dfbb085295 + - AUTH_FILE_BASED_ORGANIZATION_DISPLAY_NAME=Default + - AUTH_FILE_BASED_ORGANIZATION_REGION=us + # Signs login JWTs; must be 32 bytes (64 hex chars). Overrides the + # placeholder in platform-api-config.toml (env wins in koanf). + - AUTH_JWT_SECRET_KEY=fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210 + volumes: + - ./platform-api-config.toml:/etc/platform-api/config.toml:ro + ports: + - "${PA_HOST_PORT:-9243}:9243" + networks: [aiwscli] + +networks: + aiwscli: + driver: bridge diff --git a/tests/ai-workspace-cli-e2e/features/ai-workspace-cli.feature b/tests/ai-workspace-cli-e2e/features/ai-workspace-cli.feature new file mode 100644 index 000000000..f642872a1 --- /dev/null +++ b/tests/ai-workspace-cli-e2e/features/ai-workspace-cli.feature @@ -0,0 +1,57 @@ +Feature: AI Workspace CLI publish and persistence + As a platform user driving the `ap` CLI + I want to publish AI Workspace artifacts to platform-api + So that I can confirm the CLI create/update/read behaviour persists on the backend + + The suite boots a real platform-api (the AI Workspace backend), logs in as + admin/admin for a bearer token, creates a server-side project, and registers the + gateways the artifacts associate with. Each scenario scaffolds a project with + `ap project init`, applies the "create" demo content, reads it back with both + `get` and `list`, then applies the "edit" demo content (which adds + spec.associatedGateways) to confirm the update path persists too. + + Background: + Given the platform-api AI Workspace backend is running + And I am authenticated to the AI Workspace + + @llm-provider + Scenario: Publish and update an LLM provider through the CLI + When the "llm-provider" project artifact is initialized + And I build the "llm-provider" artifact + And I apply the "llm-provider" artifact + Then the CLI reports the "llm-provider" artifact was created + And the "llm-provider" artifact is retrievable from the AI Workspace + And the "llm-provider" artifact is listed in the AI Workspace + When I edit the "llm-provider" artifact + And I build the "llm-provider" artifact + And I re-apply the "llm-provider" artifact + Then the CLI reports the "llm-provider" artifact was updated + And the "llm-provider" artifact is associated with gateway "prod-eu-01" + + @llm-proxy + Scenario: Publish and update an LLM proxy through the CLI + When the "llm-proxy" project artifact is initialized + And I build the "llm-proxy" artifact + And I apply the "llm-proxy" artifact + Then the CLI reports the "llm-proxy" artifact was created + And the "llm-proxy" artifact is retrievable from the AI Workspace + And the "llm-proxy" artifact is listed in the AI Workspace + When I edit the "llm-proxy" artifact + And I build the "llm-proxy" artifact + And I re-apply the "llm-proxy" artifact + Then the CLI reports the "llm-proxy" artifact was updated + And the "llm-proxy" artifact is associated with gateway "prod-eu-01" + + @mcp-proxy + Scenario: Publish and update an MCP proxy through the CLI + When the "mcp-proxy" project artifact is initialized + And I build the "mcp-proxy" artifact + And I apply the "mcp-proxy" artifact + Then the CLI reports the "mcp-proxy" artifact was created + And the "mcp-proxy" artifact is retrievable from the AI Workspace + And the "mcp-proxy" artifact is listed in the AI Workspace + When I edit the "mcp-proxy" artifact + And I build the "mcp-proxy" artifact + And I re-apply the "mcp-proxy" artifact + Then the CLI reports the "mcp-proxy" artifact was updated + And the "mcp-proxy" artifact is associated with gateway "prod-eu-01" diff --git a/tests/ai-workspace-cli-e2e/go.mod b/tests/ai-workspace-cli-e2e/go.mod new file mode 100644 index 000000000..f751c659d --- /dev/null +++ b/tests/ai-workspace-cli-e2e/go.mod @@ -0,0 +1,15 @@ +module github.com/wso2/api-platform/tests/ai-workspace-cli-e2e + +go 1.26.5 + +require github.com/cucumber/godog v0.15.0 + +require ( + github.com/cucumber/gherkin/go/v26 v26.2.0 // indirect + github.com/cucumber/messages/go/v21 v21.0.1 // indirect + github.com/gofrs/uuid v4.3.1+incompatible // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-memdb v1.3.4 // indirect + github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/spf13/pflag v1.0.5 // indirect +) diff --git a/tests/ai-workspace-cli-e2e/go.sum b/tests/ai-workspace-cli-e2e/go.sum new file mode 100644 index 000000000..9514a4664 --- /dev/null +++ b/tests/ai-workspace-cli-e2e/go.sum @@ -0,0 +1,48 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cucumber/gherkin/go/v26 v26.2.0 h1:EgIjePLWiPeslwIWmNQ3XHcypPsWAHoMCz/YEBKP4GI= +github.com/cucumber/gherkin/go/v26 v26.2.0/go.mod h1:t2GAPnB8maCT4lkHL99BDCVNzCh1d7dBhCLt150Nr/0= +github.com/cucumber/godog v0.15.0 h1:51AL8lBXF3f0cyA5CV4TnJFCTHpgiy+1x1Hb3TtZUmo= +github.com/cucumber/godog v0.15.0/go.mod h1:FX3rzIDybWABU4kuIXLZ/qtqEe1Ac5RdXmqvACJOces= +github.com/cucumber/messages/go/v21 v21.0.1 h1:wzA0LxwjlWQYZd32VTlAVDTkW6inOFmSM+RuOwHZiMI= +github.com/cucumber/messages/go/v21 v21.0.1/go.mod h1:zheH/2HS9JLVFukdrsPWoPdmUtmYQAQPLk7w5vWsk5s= +github.com/cucumber/messages/go/v22 v22.0.0/go.mod h1:aZipXTKc0JnjCsXrJnuZpWhtay93k7Rn3Dee7iyPJjs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.3.1+incompatible h1:0/KbAdpx3UXAx1kEOWHJeOkpbgRFGHVgv+CFIY7dBJI= +github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-memdb v1.3.4 h1:XSL3NR682X/cVk2IeV0d70N4DZ9ljI885xAEU8IoK3c= +github.com/hashicorp/go-memdb v1.3.4/go.mod h1:uBTr1oQbtuMgd1SSGoR8YV27eT3sBHbYiNm53bMpgSg= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/tests/ai-workspace-cli-e2e/platform-api-config.toml b/tests/ai-workspace-cli-e2e/platform-api-config.toml new file mode 100644 index 000000000..0290e2c5d --- /dev/null +++ b/tests/ai-workspace-cli-e2e/platform-api-config.toml @@ -0,0 +1,26 @@ +# platform-api configuration for the combined e2e stack. +# Database settings come from environment variables (see docker-compose.yaml); +# this file supplies file-based auth so the scenario can log in (admin/admin). + +[auth.jwt] +enabled = true +issuer = "platform-api" +secret_key = "e2e-integration-secret-key-0123456789" + +[auth.file_based] +enabled = true + +[auth.file_based.organization] +id = "default" # organization handle (URL-safe slug) +display_name = "Default" +region = "us" + +# Default login: admin / admin (bcrypt hash of "admin", reused from the shipped sample config). +[[auth.file_based.users]] +username = "admin" +password_hash = "$2y$10$U2yKMwGamGwDoMu0hRPT7u8nCuP8z/qxHFOKV6dhIxkJN9NJ0eVQ." +# NOTE: platform-api ignores file-based users from a mounted config (defaultConfig's +# admin wins in koanf's slice merge), so scopes here are not authoritative. The +# @devportal stack instead injects the admin (with dp:* scopes) via the +# AUTH_FILE_BASED_USERS env var, which does override the default. See suite_test.go. +scopes = "ap:organization:manage ap:gateway:manage ap:gateway_custom_policy:manage ap:rest_api:manage ap:llm_provider:manage ap:llm_proxy:manage ap:mcp_proxy:manage ap:webbroker_api:manage ap:websub_api:manage ap:application:manage ap:subscription:manage ap:subscription_plan:manage ap:project:manage ap:llm_template:manage ap:devportal:manage ap:api_key:read ap:secret:manage" diff --git a/tests/ai-workspace-cli-e2e/resources/llm-provider/create/metadata.yaml b/tests/ai-workspace-cli-e2e/resources/llm-provider/create/metadata.yaml new file mode 100644 index 000000000..56ae7ef5e --- /dev/null +++ b/tests/ai-workspace-cli-e2e/resources/llm-provider/create/metadata.yaml @@ -0,0 +1,7 @@ +apiVersion: ai-workspace.api-platform.wso2.com/v1alpha +kind: LlmProvider +metadata: + name: claude-provider +spec: + displayName: wso2 claude provider + version: v1.0 diff --git a/tests/ai-workspace-cli-e2e/resources/llm-provider/create/runtime.yaml b/tests/ai-workspace-cli-e2e/resources/llm-provider/create/runtime.yaml new file mode 100644 index 000000000..c2151ae86 --- /dev/null +++ b/tests/ai-workspace-cli-e2e/resources/llm-provider/create/runtime.yaml @@ -0,0 +1,102 @@ +apiVersion: gateway.api-platform.wso2.com/v1 +kind: LlmProvider +metadata: + name: claude-provider +spec: + displayName: wso2 claude provider + version: v1.0 + context: /wso2-claude + template: anthropic + upstream: + url: https://api.anthropic.com + auth: + header: x-api-key + type: api-key + value: "11111" + accessControl: + mode: allow_all + policies: + - name: api-key-auth + paths: + - methods: + - '*' + params: + in: header + key: X-API-Key + path: /* + version: "" + - name: llm-cost + paths: + - methods: + - '*' + params: {} + path: /* + version: v1 + - name: advanced-ratelimit + paths: + - methods: + - '*' + params: + quotas: + - limits: + - duration: 1h + limit: 10 + name: request-limit + path: /v1/experimental/generate_prompt + version: "" + - name: token-based-ratelimit + paths: + - methods: + - '*' + params: + totalTokenLimits: + - count: 5 + duration: 1h + path: /v1/experimental/improve_prompt + version: "" + - name: llm-cost-based-ratelimit + paths: + - methods: + - '*' + params: + budgetLimits: + - amount: 2 + duration: 1h + path: /v1/experimental/improve_prompt + version: "" + - name: llm-cost + paths: + - methods: + - '*' + params: {} + path: /* + version: "" + - name: advanced-ratelimit + paths: + - methods: + - '*' + params: + quotas: + - keyExtraction: + - type: routename + - key: x-wso2-application-id + type: metadata + limits: + - duration: 2h + limit: 100 + name: consumer-request-limit + path: /v1/experimental/improve_prompt + - methods: + - '*' + params: + quotas: + - keyExtraction: + - type: routename + - key: x-wso2-application-id + type: metadata + limits: + - duration: 1h + limit: 1 + name: consumer-request-limit + path: /v1/files + version: "" diff --git a/tests/ai-workspace-cli-e2e/resources/llm-provider/definition.yaml b/tests/ai-workspace-cli-e2e/resources/llm-provider/definition.yaml new file mode 100644 index 000000000..8d16d8011 --- /dev/null +++ b/tests/ai-workspace-cli-e2e/resources/llm-provider/definition.yaml @@ -0,0 +1,4457 @@ +openapi: 3.0.0 +info: + title: Anthropic Claude API + version: 1.0.0 + description: | + The Anthropic Claude API provides access to Claude, a family of large language models capable of performing a wide range of natural language processing tasks including chat completion, tool use, code execution, and document analysis. + The API supports both real-time and batch interactions, with advanced configuration options for tool usage, streaming, caching, and system-level reasoning. + x-marketplace-tags: + - name: AI + description: AI and LLM APIs + +servers: + - url: https://api.anthropic.com + +components: + securitySchemes: + ApiKeyAuth: + type: apiKey + in: header + name: x-api-key + description: Your unique API key for authentication. Required in the header of all API requests to authenticate your account and access Anthropic's services. Get your API key through the Console. Each key is scoped to a Workspace. + + parameters: + AnthropicVersionHeader: + in: header + name: anthropic-version + description: The version of the Anthropic API you want to use. Required header for all API requests. Must be set to "2023-06-01". + required: true + schema: + type: string + enum: + - "2023-06-01" + + AnthropicBetaHeader: + in: header + name: anthropic-beta + description: Optional header to specify beta feature(s) you want to use. Multiple betas can be specified using a comma-separated list or by including the header multiple times. + required: false + schema: + type: array + items: + type: string + enum: + - "computer-use-2024-10-22" + - "computer-use-2025-01-24" + - "output-128k-2025-02-19" + - "token-efficient-tools-2025-02-19" + - "prompt-tools-2025-04-02" + - "interleaved-thinking-2025-05-14" + - "files-api-2025-04-14" + + AnthropicDangerousDirectBrowserAccessHeader: + in: header + name: anthropic-dangerous-direct-browser-access + description: This header must be set to "true" to enable direct client-side (browser) access to the API. This acknowledges the security risks of exposing your API key and is not recommended for production environments. + required: false + schema: + type: boolean + + BeforeId: + name: before_id + in: query + schema: + type: string + description: ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately before this object. + + AfterId: + name: after_id + in: query + schema: + type: string + description: ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately after this object. + + Limit: + name: limit + in: query + schema: + type: integer + default: 20 + minimum: 1 + maximum: 1000 + description: Number of items to return per page. + + BatchId: + name: message_batch_id + in: path + required: true + schema: + type: string + + FileId: + name: file_id + in: path + required: true + schema: + type: string + description: ID of the File. + + UserId: + name: user_id + in: path + required: true + schema: + type: string + description: ID of the User. + + InviteId: + name: invite_id + in: path + required: true + schema: + type: string + description: ID of the Invite. + + WorkspaceId: + name: workspace_id + in: path + required: true + schema: + type: string + description: ID of the Workspace. + + ApiKeyId: + name: api_key_id + in: path + required: true + schema: + type: string + description: ID of the API key. + + headers: + RequestId: + description: A unique identifier for the request. + schema: + type: string + + AnthropicOrganizationId: + description: The ID of the organization that the request belongs to. + schema: + type: string + + RetryAfter: + description: The number of seconds until you can retry the request. + schema: + type: integer + + AnthropicRateLimitRequestsLimit: + description: The maximum number of requests allowed within any rate limit period. + schema: + type: integer + + AnthropicRateLimitRequestsRemaining: + description: The number of requests remaining before being rate limited. + schema: + type: integer + + AnthropicRateLimitRequestsReset: + description: The time when the request rate limit will reset, provided in RFC 3339 format. + schema: + type: string + format: date-time + + AnthropicRateLimitTokensLimit: + description: The maximum number of tokens allowed within any rate limit period. + schema: + type: integer + + AnthropicRateLimitTokensRemaining: + description: The number of tokens remaining (rounded to the nearest thousand) before being rate limited. + schema: + type: integer + + AnthropicRateLimitTokensReset: + description: The time when the token rate limit will reset, provided in RFC 3339 format. + schema: + type: string + format: date-time + + AnthropicRateLimitInputTokensLimit: + description: The maximum number of input tokens allowed within any rate limit period. + schema: + type: integer + + AnthropicRateLimitInputTokensRemaining: + description: The number of input tokens remaining (rounded to the nearest thousand) before being rate limited. + schema: + type: integer + + AnthropicRateLimitInputTokensReset: + description: The time when the input token rate limit will reset, provided in RFC 3339 format. + schema: + type: string + format: date-time + + AnthropicRateLimitOutputTokensLimit: + description: The maximum number of output tokens allowed within any rate limit period. + schema: + type: integer + + AnthropicRateLimitOutputTokensRemaining: + description: The number of output tokens remaining (rounded to the nearest thousand) before being rate limited. + schema: + type: integer + + AnthropicRateLimitOutputTokensReset: + description: The time when the output token rate limit will reset, provided in RFC 3339 format. + schema: + type: string + format: date-time + + AnthropicPriorityInputTokensLimit: + description: The maximum number of Priority Tier input tokens allowed within any rate limit period. (Priority Tier only) + schema: + type: integer + + AnthropicPriorityInputTokensRemaining: + description: The number of Priority Tier input tokens remaining (rounded to the nearest thousand) before being rate limited. (Priority Tier only) + schema: + type: integer + + AnthropicPriorityInputTokensReset: + description: The time when the Priority Tier input token rate limit will be fully replenished, provided in RFC 3339 format. (Priority Tier only) + schema: + type: string + format: date-time + + AnthropicPriorityOutputTokensLimit: + description: The maximum number of Priority Tier output tokens allowed within any rate limit period. (Priority Tier only) + schema: + type: integer + + AnthropicPriorityOutputTokensRemaining: + description: The number of Priority Tier output tokens remaining (rounded to the nearest thousand) before being rate limited. (Priority Tier only) + schema: + type: integer + + AnthropicPriorityOutputTokensReset: + description: The time when the Priority Tier output token rate limit will be fully replenished, provided in RFC 3339 format. (Priority Tier only) + schema: + type: string + format: date-time + + schemas: + CacheControl: + type: object + nullable: true + required: + - type + properties: + type: + type: string + enum: [ephemeral] + ttl: + type: string + enum: [5m, 1h] + description: Controls caching behavior for this content + + CharacterLocation: + type: object + required: + - cited_text + - document_index + - document_title + - end_char_index + - start_char_index + - type + properties: + cited_text: + type: string + description: The text being cited + document_index: + type: integer + description: Should be greater than 0 + minimum: 1 + document_title: + type: string + nullable: true + minLength: 1 + maxLength: 256 + end_char_index: + type: integer + start_char_index: + type: integer + description: Should be greater than 0 + minimum: 1 + type: + type: string + enum: [char_location] + default: char_location + + PageLocation: + type: object + required: + - cited_text + - document_index + - document_title + - end_page_number + - start_page_number + - type + properties: + cited_text: + type: string + description: The text being cited + document_index: + type: integer + description: Should be greater than 0 + minimum: 1 + document_title: + type: string + nullable: true + minLength: 1 + maxLength: 256 + end_page_number: + type: integer + start_page_number: + type: integer + description: Should be greater than 0 + minimum: 1 + type: + type: string + enum: [page_location] + default: page_location + + ContentBlockLocation: + type: object + required: + - cited_text + - document_index + - document_title + - end_block_index + - start_block_index + - type + properties: + cited_text: + type: string + description: The text being cited + document_index: + type: integer + description: Should be greater than 0 + minimum: 1 + document_title: + type: string + nullable: true + minLength: 1 + maxLength: 256 + end_block_index: + type: integer + start_block_index: + type: integer + description: Should be greater than 0 + minimum: 1 + type: + type: string + enum: [content_block_location] + + RequestWebSearchResultLocationCitation: + type: object + required: + - cited_text + - encrypted_index + - title + - type + - url + properties: + cited_text: + type: string + encrypted_index: + type: string + title: + type: string + nullable: true + minLength: 1 + maxLength: 256 + type: + type: string + enum: [web_search_result_location] + url: + type: string + minLength: 1 + maxLength: 2048 + + Citations: + type: array + nullable: true + description: Citations supporting the text block. + items: + oneOf: + - $ref: '#/components/schemas/CharacterLocation' + - $ref: '#/components/schemas/PageLocation' + - $ref: '#/components/schemas/ContentBlockLocation' + - $ref: '#/components/schemas/RequestWebSearchResultLocationCitation' + + ServerToolUse: + type: object + required: + - id + - input + - name + - type + properties: + id: + type: string + input: + type: object + name: + type: string + enum: [web_search, code_execution] + cache_control: + $ref: '#/components/schemas/CacheControl' + + WebSearchToolResultObject: + type: object + required: + - error_code + - type + properties: + error_code: + type: string + enum: [invalid_tool_input, unavailable, max_uses_exceeded, too_many_requests, query_too_long] + type: + type: string + enum: [web_search_tool_result_error] + + WebSearchToolResultObjectArray: + type: array + items: + type: object + required: + - encrypted_content + - page_age + - title + - type + - url + properties: + encrypted_content: + type: string + page_age: + type: string + nullable: true + title: + type: string + minLength: 1 + type: + type: string + enum: [web_search_result] + url: + type: string + minLength: 1 + + WebSearchToolResult: + type: object + required: + - content + - tool_use_id + - type + properties: + content: + oneOf: + - $ref: '#/components/schemas/WebSearchToolResultObject' + - $ref: '#/components/schemas/WebSearchToolResultObjectArray' + tool_use_id: + type: string + type: + type: string + enum: [web_search_tool_result] + cache_control: + $ref: '#/components/schemas/CacheControl' + + RequestCodeExecutionToolResultBlockErrorContent: + type: object + required: + - error_code + - type + properties: + error_code: + type: string + enum: [invalid_tool_input, unavailable, max_uses_exceeded, too_many_requests, execution_time_exceeded] + type: + type: string + enum: [code_execution_tool_result_error] + + RequestCodeExecutionToolResultBlockContent: + type: object + required: + - content + - return_code + - stderr + - stdout + - type + properties: + content: + type: array + items: + type: object + required: + - file_id + - type + properties: + file_id: + type: string + type: + type: string + enum: [code_execution_output] + return_code: + type: integer + stderr: + type: string + stdout: + type: string + type: + type: string + enum: [code_execution_result] + + RequestCodeExecutionToolResultBlock: + type: object + required: + - content + - tool_use_id + - type + properties: + content: + type: object + oneOf: + - $ref: '#/components/schemas/RequestCodeExecutionToolResultBlockErrorContent' + - $ref: '#/components/schemas/RequestCodeExecutionToolResultBlockContent' + tool_use_id: + type: string + type: + type: string + enum: [code_execution_tool_result] + cache_control: + $ref: '#/components/schemas/CacheControl' + + RequestMCPToolUseBlock: + type: object + required: + - id + - input + - name + - server_name + - type + properties: + id: + type: string + input: + type: object + name: + type: string + server_name: + type: string + type: + type: string + enum: [mcp_tool_use] + cache_control: + $ref: '#/components/schemas/CacheControl' + + RequestMCPToolResultBlockContentObject: + type: object + required: + - text + - type + properties: + text: + type: string + minLength: 1 + type: + type: string + enum: [text] + cache_control: + $ref: '#/components/schemas/CacheControl' + citations: + $ref: '#/components/schemas/Citations' + + RequestMCPToolResultBlock: + type: object + required: + - tool_use_id + - type + properties: + tool_use_id: + type: string + type: + type: string + enum: [mcp_tool_result] + cache_control: + $ref: '#/components/schemas/CacheControl' + content: + oneOf: + - type: string + - type: array + items: + $ref: '#/components/schemas/RequestMCPToolResultBlockContentObject' + is_error: + type: boolean + + Text: + type: object + required: + - text + - type + properties: + text: + type: string + minLength: 1 + type: + type: string + enum: [text] + cache_control: + $ref: '#/components/schemas/CacheControl' + citations: + $ref: '#/components/schemas/Citations' + + ImageBase64: + type: object + required: + - data + - media_type + - type + properties: + data: + type: string + media_type: + type: string + enum: [image/jpeg, image/png, image/gif, image/webp] + type: + type: string + enum: [base64] + + URL: + type: object + required: + - type + - url + properties: + type: + type: string + enum: [url] + url: + type: string + + File: + type: object + required: + - file_id + - type + properties: + file_id: + type: string + type: + type: string + enum: [file] + + Image: + type: object + required: + - source + - type + properties: + source: + oneOf: + - $ref: '#/components/schemas/ImageBase64' + - $ref: '#/components/schemas/URL' + - $ref: '#/components/schemas/File' + type: + type: string + enum: [image] + cache_control: + $ref: '#/components/schemas/CacheControl' + + ToolUse: + type: object + required: + - id + - input + - name + - type + properties: + id: + type: string + input: + type: object + name: + type: string + minLength: 1 + maxLength: 200 + type: + type: string + enum: [tool_use] + cache_control: + $ref: '#/components/schemas/CacheControl' + + ToolResult: + type: object + required: + - tool_use_id + - type + properties: + tool_use_id: + type: string + type: + type: string + enum: [tool_result] + cache_control: + $ref: '#/components/schemas/CacheControl' + content: + oneOf: + - type: string + - type: object + oneOf: + - $ref: '#/components/schemas/Text' + - $ref: '#/components/schemas/Image' + is_error: + type: boolean + + Base64PDF: + type: object + required: + - data + - media_type + - type + properties: + data: + type: string + media_type: + type: string + enum: [application/pdf] + type: + type: string + enum: [base64] + + PlainTextPDF: + type: object + required: + - data + - media_type + - type + properties: + data: + type: string + media_type: + type: string + enum: [text/plain] + type: + type: string + enum: [text] + + ContentBlockPDF: + type: object + required: + - content + - type + properties: + content: + oneOf: + - type: string + - type: object + oneOf: + - $ref: '#/components/schemas/Text' + - $ref: '#/components/schemas/Image' + type: + type: string + enum: [content] + + Document: + type: object + required: + - source + - type + properties: + source: + type: object + oneOf: + - $ref: '#/components/schemas/Base64PDF' + - $ref: '#/components/schemas/PlainTextPDF' + - $ref: '#/components/schemas/ContentBlockPDF' + - $ref: '#/components/schemas/URL' + - $ref: '#/components/schemas/File' + type: + type: string + enum: [document] + cache_control: + $ref: '#/components/schemas/CacheControl' + citations: + type: object + properties: + enabled: + type: boolean + context: + type: string + nullable: true + minLength: 1 + title: + type: string + nullable: true + minLength: 1 + maxLength: 500 + + Thinking: + type: object + required: + - signature + - thinking + - type + properties: + signature: + type: string + thinking: + type: string + type: + type: string + enum: [thinking] + + RedactedThinking: + type: object + required: + - data + - type + properties: + data: + type: string + type: + type: string + enum: [redacted_thinking] + + RequestContainerUploadBlock: + type: object + required: + - file_id + - type + properties: + file_id: + type: string + type: + type: string + enum: [container_upload] + cache_control: + $ref: '#/components/schemas/CacheControl' + + Message: + type: object + required: + - role + - content + properties: + role: + type: string + enum: [user, assistant] + content: + oneOf: + - type: string + - type: array + items: + type: object + required: + - type + oneOf: + - $ref: '#/components/schemas/ServerToolUse' + - $ref: '#/components/schemas/WebSearchToolResult' + - $ref: '#/components/schemas/RequestCodeExecutionToolResultBlock' + - $ref: '#/components/schemas/RequestMCPToolUseBlock' + - $ref: '#/components/schemas/Text' + - $ref: '#/components/schemas/Image' + - $ref: '#/components/schemas/ToolUse' + - $ref: '#/components/schemas/ToolResult' + - $ref: '#/components/schemas/Document' + - $ref: '#/components/schemas/Thinking' + - $ref: '#/components/schemas/RedactedThinking' + - $ref: '#/components/schemas/RequestContainerUploadBlock' + + ToolConfiguration: + type: object + nullable: true + properties: + allowed_tools: + type: array + nullable: true + items: + type: string + enabled: + type: boolean + nullable: true + + MCPServers: + type: object + required: + - name + - type + properties: + name: + type: string + type: + type: string + enum: [url] + url: + type: string + authorization_token: + type: string + nullable: true + tool_configuration: + $ref: '#/components/schemas/ToolConfiguration' + + ThinkingEnabled: + type: object + required: + - type + properties: + type: + type: string + enum: [enabled] + budget_tokens: + type: integer + minimum: 1024 + description: Tokens allocated for Claude's internal reasoning. Must be at least 1024 and cannot exceed max_tokens value. + + ThinkingDisabled: + type: object + required: + - type + properties: + type: + type: string + enum: [disabled] + + ToolChoiceAuto: + type: object + description: Automatically choose whether and which tool to use + required: + - type + properties: + type: + type: string + enum: [auto] + disable_parallel_tool_use: + type: boolean + default: false + description: Whether to disable parallel tool use + + ToolChoiceAny: + type: object + description: Automatically choose whether and which tool to use + required: + - type + properties: + type: + type: string + enum: [any] + disable_parallel_tool_use: + type: boolean + default: false + description: Whether to disable parallel tool use + + ToolChoiceTool: + type: object + description: Automatically choose whether and which tool to use + required: + - name + - type + properties: + name: + type: string + description: The name of the tool to use + type: + type: string + enum: [tool] + disable_parallel_tool_use: + type: boolean + default: false + description: Whether to disable parallel tool use + + ToolChoiceNone: + type: object + description: Do not use any tools + required: + - type + properties: + type: + type: string + enum: [none] + + ToolInputSchema: + type: object + required: + - type + properties: + type: + type: string + enum: [object] + properties: + type: object + nullable: true + + Tool: + type: object + required: + - input_schema + - name + properties: + input_schema: + $ref: '#/components/schemas/ToolInputSchema' + name: + type: string + minLength: 1 + maxLength: 64 + cache_control: + $ref: '#/components/schemas/CacheControl' + description: + type: string + type: + type: string + nullable: true + enum: [custom] + + ComputerUseTool20241022: + type: object + required: + - display_height_px + - display_width_px + - name + - type + properties: + display_height_px: + type: integer + minimum: 2 + display_width_px: + type: integer + minimum: 2 + name: + type: string + enum: [computer] + type: + type: string + enum: [computer_20241022] + cache_control: + $ref: '#/components/schemas/CacheControl' + display_number: + type: integer + nullable: true + minimum: 1 + + BashTool20241022: + type: object + required: + - name + - type + properties: + name: + type: string + enum: [bash] + type: + type: string + enum: [bash_20241022] + cache_control: + $ref: '#/components/schemas/CacheControl' + + TextEditorTool20241022: + type: object + required: + - name + - type + properties: + name: + type: string + enum: [str_replace_editor] + type: + type: string + enum: [text_editor_20241022] + cache_control: + $ref: '#/components/schemas/CacheControl' + + ComputerUseTool20250124: + type: object + required: + - display_height_px + - display_width_px + - name + - type + properties: + display_height_px: + type: integer + minimum: 2 + display_width_px: + type: integer + minimum: 2 + name: + type: string + enum: [computer] + type: + type: string + enum: [computer_20250124] + cache_control: + $ref: '#/components/schemas/CacheControl' + display_number: + type: integer + nullable: true + minimum: 1 + + BashTool20250124: + type: object + required: + - name + - type + properties: + name: + type: string + enum: [bash] + type: + type: string + enum: [bash_20250124] + cache_control: + $ref: '#/components/schemas/CacheControl' + + TextEditorTool20250124: + type: object + required: + - name + - type + properties: + name: + type: string + enum: [str_replace_editor] + type: + type: string + enum: [text_editor_20250124] + cache_control: + $ref: '#/components/schemas/CacheControl' + + TextEditor20250429: + type: object + required: + - name + - type + properties: + name: + type: string + enum: [str_replace_based_edit_tool] + type: + type: string + enum: [text_editor_20250429] + cache_control: + $ref: '#/components/schemas/CacheControl' + + UserLocation: + type: object + nullable: true + properties: + type: + type: string + enum: [approximate] + city: + type: string + nullable: true + minLength: 1 + maxLength: 256 + country: + type: string + nullable: true + minLength: 2 + maxLength: 2 + description: ISO Code of the country. + region: + type: string + nullable: true + minLength: 1 + maxLength: 256 + timezone: + type: string + nullable: true + minLength: 1 + maxLength: 256 + description: IANA timezone of the user. + + WebSearchTool20250305: + type: object + required: + - name + - type + properties: + name: + type: string + enum: [web_search] + type: + type: string + enum: [web_search_20250305] + allowed_domains: + type: array + nullable: true + items: + type: string + description: If provided, only these domains will be included in results. Cannot be used alongside blocked_domains. + blocked_domains: + type: array + nullable: true + items: + type: string + description: If provided, these domains will never appear in results. Cannot be used alongside allowed_domains. + cache_control: + $ref: '#/components/schemas/CacheControl' + max_uses: + type: integer + nullable: true + minimum: 1 + user_location: + $ref: '#/components/schemas/UserLocation' + + CodeExecutionTool20250522: + type: object + required: + - name + - type + properties: + name: + type: string + enum: [code_execution] + type: + type: string + enum: [code_execution_20250522] + cache_control: + $ref: '#/components/schemas/CacheControl' + + Container: + type: object + nullable: true + required: + - expires_at + - id + properties: + expires_at: + type: string + format: date-time + id: + type: string + + TextResponse: + type: object + required: + - citations + - text + - type + properties: + citations: + $ref: '#/components/schemas/Citations' + text: + type: string + maxLength: 5000000 + type: + type: string + enum: [text] + + ToolUseResponse: + type: object + required: + - id + - input + - name + - type + properties: + id: + type: string + input: + type: object + name: + type: string + minLength: 1 + type: + type: string + enum: [tool_use] + + ResponseServerToolUseBlock: + type: object + required: + - id + - input + - name + - type + properties: + id: + type: string + input: + type: object + name: + type: string + minLength: 1 + type: + type: string + enum: [server_tool_use] + + ResponseWebSearchToolResultBlock: + type: object + required: + - content + - tool_use_id + - type + properties: + content: + oneOf: + - $ref: '#/components/schemas/WebSearchToolResultObject' + - $ref: '#/components/schemas/WebSearchToolResultObjectArray' + tool_use_id: + type: string + type: + type: string + enum: [web_search_tool_result] + + ResponseCodeExecutionToolResultBlock: + type: object + required: + - content + - tool_use_id + - type + properties: + content: + type: object + oneOf: + - $ref: '#/components/schemas/RequestCodeExecutionToolResultBlockErrorContent' + - $ref: '#/components/schemas/RequestCodeExecutionToolResultBlockContent' + tool_use_id: + type: string + type: + type: string + enum: [code_execution_tool_result] + + ResponseMCPToolUseBlock: + type: object + required: + - id + - input + - name + - server_name + - type + properties: + id: + type: string + input: + type: object + name: + type: string + server_name: + type: string + type: + type: string + enum: [mcp_tool_use] + + ResponseMCPToolResultBlock: + type: object + required: + - content + - is_error + - tool_use_id + - type + properties: + content: + oneOf: + - type: string + - $ref: '#/components/schemas/TextResponse' + is_error: + type: boolean + default: false + tool_use_id: + type: string + type: + type: string + enum: [mcp_tool_result] + + ResponseContainerUploadBlock: + type: object + required: + - file_id + - type + properties: + file_id: + type: string + type: + type: string + enum: [container_upload] + + CacheCreationUsage: + type: object + nullable: true + required: + - ephemeral_1h_input_tokens + - ephemeral_5m_input_tokens + properties: + ephemeral_1h_input_tokens: + type: integer + default: 0 + minimum: 0 + ephemeral_5m_input_tokens: + type: integer + default: 0 + minimum: 0 + + ServerToolUseUsage: + type: object + nullable: true + required: + - web_search_requests + properties: + web_search_requests: + type: integer + default: 0 + minimum: 0 + description: The number of web search tool requests. + + Usage: + type: object + required: + - cache_creation + - cache_creation_input_tokens + - cache_read_input_tokens + - input_tokens + - output_tokens + - server_tool_use + description: | + Billing and rate-limit usage. + properties: + cache_creation: + $ref: '#/components/schemas/CacheCreationUsage' + cache_creation_input_tokens: + type: integer + nullable: true + minimum: 1 + cache_read_input_tokens: + type: integer + nullable: true + minimum: 1 + input_tokens: + type: integer + minimum: 1 + output_tokens: + type: integer + minimum: 1 + server_tool_use: + $ref: '#/components/schemas/ServerToolUseUsage' + service_tier: + type: string + nullable: true + enum: [standard, priority, batch] + + MessageRequest: + type: array + description: A list of messages comprising the conversation so far. + items: + $ref: '#/components/schemas/Message' + maxItems: 100000 + + ModelRequest: + type: string + minLength: 1 + maxLength: 256 + description: The model that will complete your prompt. Must be between 1 and 256 characters. + + SystemRequest: + oneOf: + - type: string + - type: object # Move to Content + required: + - text + - type + properties: + text: + type: string + minLength: 1 + type: + type: string + enum: [text] + cache_control: + $ref: '#/components/schemas/CacheControl' + citations: + $ref: '#/components/schemas/Citations' + description: A system prompt providing context and instructions to Claude, such as specifying a particular goal or role. Can be provided either as a simple string or as a structured object with cache control options. + + ThinkingRequest: + oneOf: + - $ref: '#/components/schemas/ThinkingEnabled' + - $ref: '#/components/schemas/ThinkingDisabled' + + ToolChoiceRequest: + oneOf: + - $ref: '#/components/schemas/ToolChoiceAuto' + - $ref: '#/components/schemas/ToolChoiceAny' + - $ref: '#/components/schemas/ToolChoiceTool' + - $ref: '#/components/schemas/ToolChoiceNone' + + ToolsRequest: + type: array + items: + oneOf: + - $ref: '#/components/schemas/Tool' + - $ref: '#/components/schemas/ComputerUseTool20241022' + - $ref: '#/components/schemas/BashTool20241022' + - $ref: '#/components/schemas/TextEditorTool20241022' + - $ref: '#/components/schemas/ComputerUseTool20250124' + - $ref: '#/components/schemas/BashTool20250124' + - $ref: '#/components/schemas/TextEditorTool20250124' + - $ref: '#/components/schemas/TextEditor20250429' + - $ref: '#/components/schemas/WebSearchTool20250305' + - $ref: '#/components/schemas/CodeExecutionTool20250522' + description: List of tools that the model may use during the conversation + + MessageRequestBody: + type: object + required: + - max_tokens + - messages + - model + properties: + max_tokens: + type: integer + minimum: 1 + description: The maximum number of tokens to generate before stopping. Models may stop before reaching this maximum. + messages: + $ref: '#/components/schemas/MessageRequest' + model: + $ref: '#/components/schemas/ModelRequest' + container: + type: string + nullable: true + description: Container identifier for reuse across requests. + mcp_servers: + $ref: '#/components/schemas/MCPServers' + metadata: + type: object + properties: + user_id: + type: string + nullable: true + description: A user identifier for tracing the request. + maxLength: 256 + description: An object describing metadata about the request. Can include user_id for tracking purposes. + service_tier: + type: string + enum: [auto, standard_only] + description: 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 for details. + stop_sequences: + type: array + items: + type: string + description: Custom text sequences that will cause the model to stop generating. The model will stop when it encounters any of these sequences. + stream: + type: boolean + description: Whether to incrementally stream the response using server-sent events. + system: + $ref: '#/components/schemas/SystemRequest' + temperature: + type: number + minimum: 0 + maximum: 1 + description: Amount of randomness injected into the response. Defaults to 1.0. Use lower values for analytical tasks and higher values for creative tasks. + thinking: + $ref: '#/components/schemas/ThinkingRequest' + tool_choice: + $ref: '#/components/schemas/ToolChoiceRequest' + tools: + $ref: '#/components/schemas/ToolsRequest' + top_k: + type: integer + minimum: 0 + description: Only sample from the top K options for each subsequent token + top_p: + type: number + minimum: 0 + maximum: 1 + description: Only sample from tokens with cumulative probability less than this value + + MessageResponse: + type: object + required: + - container + - content + - id + - model + - role + - stop_reason + - stop_sequence + - type + - usage + properties: + container: + $ref: '#/components/schemas/Container' + content: + type: array + description: Content generated by the model. This is an array of content blocks, each of which has a type that determines its shape. + items: + oneOf: + - $ref: '#/components/schemas/TextResponse' + - $ref: '#/components/schemas/ToolUseResponse' + - $ref: '#/components/schemas/ResponseServerToolUseBlock' + - $ref: '#/components/schemas/ResponseWebSearchToolResultBlock' + - $ref: '#/components/schemas/ResponseCodeExecutionToolResultBlock' + - $ref: '#/components/schemas/ResponseMCPToolResultBlock' + - $ref: '#/components/schemas/ResponseContainerUploadBlock' + - $ref: '#/components/schemas/Thinking' + - $ref: '#/components/schemas/RedactedThinking' + id: + type: string + description: Unique object identifier. The format and length of IDs may change over time. + model: + type: string + minLength: 1 + maxLength: 256 + description: The model that handled the request. + role: + type: string + enum: [assistant] + default: assistant + description: Conversational role of the generated message. This will always be "assistant". + stop_reason: + type: string + nullable: true + enum: [end_turn, max_tokens, stop_sequence, tool_use, pause_turn, refusal] + description: | + The reason that we stopped. This may be one of 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 + + 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: + type: string + nullable: true + description: 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: + type: string + enum: [message] + default: message + description: Object type. For Messages, this is always "message". + usage: + $ref: '#/components/schemas/Usage' + + ModelResponseData: + type: object + required: + - created_at + - display_name + - id + - type + properties: + created_at: + type: string + format: date-time + description: 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: + type: string + description: A human-readable name for the model. + id: + type: string + description: Unique model identifier. + type: + type: string + enum: [model] + default: model + description: Object type. For Models, this is always "model". + + FirstId: + type: string + nullable: true + description: First ID in the data list. Can be used as the before_id for the previous page. + + HasMore: + type: boolean + description: Indicates if there are more results in the requested page direction. + + LastId: + type: string + nullable: true + description: Last ID in the data list. Can be used as the after_id for the next page. + + RequestsCount: + type: object + required: + - canceled + - errored + - expired + - processing + - succeeded + properties: + canceled: + type: integer + default: 0 + errored: + type: integer + default: 0 + expired: + type: integer + default: 0 + processing: + type: integer + default: 0 + succeeded: + type: integer + default: 0 + + MessageBatchResponse: + type: object + required: + - archived_at + - cancel_initiated_at + - created_at + - ended_at + - expires_at + - id + - processing_status + - request_counts + - results_url + - type + properties: + archived_at: + type: string + format: date-time + nullable: true + cancel_initiated_at: + type: string + format: date-time + nullable: true + created_at: + type: string + format: date-time + ended_at: + type: string + format: date-time + nullable: true + expires_at: + type: string + format: date-time + id: + type: string + processing_status: + type: string + enum: [in_progress, canceling, ended] + request_counts: + $ref: '#/components/schemas/RequestsCount' + results_url: + type: string + nullable: true + type: + type: string + enum: [message_batch] + default: message_batch + + MessageBatchSucceededResult: + type: object + required: + - message + - type + properties: + message: + $ref: '#/components/schemas/MessageResponse' + type: + type: string + enum: [succeeded] + + MessageBatchError: + type: object + required: + - type + - error + properties: + type: + type: string + enum: [error] + error: + type: object + required: + - message + - type + properties: + message: + type: string # TODO: IN THE FUTURE THIS WILL BE A "oneOf" of defined error types to specify the error messages + type: + type: string + enum: + - invalid_request_error + - authentication_error + - billing_error + - permission_error + - not_found_error + - rate_limit_error + - timeout_error + - internal_server_error + - overloaded_error + + MessageBatchErroredResult: + type: object + required: + - error + - type + properties: + type: + type: string + enum: [errored] + error: + $ref: '#/components/schemas/MessageBatchError' + + MessageBatchCanceledResult: + type: object + required: + - type + properties: + type: + type: string + enum: [canceled] + + MessageBatchExpiredResult: + type: object + required: + - type + properties: + type: + type: string + enum: [expired] + + FileData: + type: object + required: + - created_at + - filename + - id + - mime_type + - size_bytes + - type + properties: + created_at: + type: string + format: date-time + description: RFC 3339 datetime string representing when the file was created + filename: + type: string + minLength: 1 + maxLength: 500 + description: Original filename of the uploaded file + id: + type: string + description: | + Unique object identifier. + The format and length of IDs may change over time. + mime_type: + type: string + minLength: 1 + maxLength: 255 + description: MIME type of the file + size_bytes: + type: integer + minimum: 0 + description: Size of the file in bytes + type: + type: string + enum: [ "file" ] + downloadable: + type: boolean + default: false + description: Whether the file can be downloaded + + AdminMemberManagementUserData: + type: object + required: + - added_at + - email + - id + - name + - role + - type + properties: + added_at: + type: string + format: date-time + description: RFC 3339 datetime string indicating when the User joined the Organization. + email: + type: string + description: Email of the User. + id: + type: string + description: ID of the User. + name: + type: string + description: Name of the User. + role: + type: string + enum: [user, developer, billing, admin] + description: Organization role of the User. + type: + type: string + enum: [user] + default: user + description: Object type. For Users, this is always "user". + + AdminInviteData: + type: object + required: + - email + - expires_at + - id + - invited_at + - role + - status + - type + properties: + email: + type: string + description: Email of the User. + expires_at: + type: string + description: RFC 3339 datetime string indicating when the invite expires. + id: + type: string + description: ID of the User. + invited_at: + type: string + description: RFC 3339 datetime string indicating when the invite was created. + role: + type: string + enum: [user, developer, billing, admin] + description: Organization role of the User. + status: + type: string + enum: [accepted, expired, deleted, pending] + description: Status of the invite. + type: + type: string + enum: [invite] + default: invite + description: Object type. For Invites, this is always "invite". + + AdminWorkspaceData: + type: object + required: + - archived_at + - created_at + - display_color + - id + - name + - type + properties: + archived_at: + type: string + nullable: true + description: RFC 3339 datetime string indicating when the Workspace was archived. + created_at: + type: string + display_color: + type: string + description: Hexadecimal color code for the Workspace. + id: + type: string + name: + type: string + type: + type: string + enum: [workspace] + default: workspace + + AdminWorkspaceMemberData: + type: object + required: + - type + - user_id + - workspace_id + - workspace_role + properties: + type: + type: string + enum: [workspace_member] + default: workspace_member + description: Object type. For Workspace Members, this is always "workspace_member". + user_id: + type: string + description: ID of the User. + workspace_id: + type: string + description: ID of the Workspace. + workspace_role: + type: string + enum: [workspace_user, workspace_developer, workspace_admin, workspace_billing] + description: Role of the Workspace Member. + + AdminAPIKeyData: + type: object + required: + - created_at + - created_by + - id + - name + - partial_key_hint + - status + - type + - workspace_id + properties: + created_at: + type: string + description: RFC 3339 datetime string indicating when the API Key was created. + created_by: + type: object + required: + - id + - type + properties: + id: + type: string + description: ID of the actor that created the object. + type: + type: string + description: Type of the actor that created the object. + description: The ID and type of the actor that created the API Key. + id: + type: string + description: ID of the API key. + name: + type: string + description: Name of the API key. + partial_key_hint: + type: string + nullable: true + description: Partially redacted hint for the API key. + status: + type: string + enum: [active, inactive, archived] + description: Status of the API key. + type: + type: string + enum: [api_key] + default: api_key + description: Object type. For API Keys, this is always "api_key". + workspace_id: + type: string + nullable: true + description: ID of the Workspace associated with the API key, or null if the API key belongs to the default Workspace. + + Error: + type: object + required: + - type + - error + properties: + type: + type: string + enum: [error] + error: + type: object + required: + - type + - message + properties: + type: + type: string + enum: + - invalid_request_error + - authentication_error + - permission_error + - not_found_error + - request_too_large + - rate_limit_error + - api_error + - overloaded_error + message: + type: string + + responses: + Error400: + description: Invalid Request Error - There was an issue with the format or content of your request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + Error401: + description: Authentication Error - There's an issue with your API key + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + Error403: + description: Permission Error - Your API key does not have permission to use the specified resource + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + Error404: + description: Not Found Error - The requested resource was not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + Error413: + description: Request Too Large - Request exceeds the maximum allowed number of bytes + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + Error429: + description: Rate Limit Error - Your account has hit a rate limit + headers: + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + Error500: + description: API Error - An unexpected error has occurred internal to Anthropic's systems + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + Error529: + description: Overloaded Error - Anthropic's API is temporarily overloaded + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + +paths: + /v1/messages: + post: + tags: + - Messages + - text-generation + summary: Create a chat completion + description: Creates a chat completion with the specified model and parameters. The response is generated based on the provided messages and system prompt. + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/AnthropicBetaHeader' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/MessageRequestBody' + responses: + '200': + description: Successful response + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + anthropic-ratelimit-requests-limit: + $ref: '#/components/headers/AnthropicRateLimitRequestsLimit' + anthropic-ratelimit-requests-remaining: + $ref: '#/components/headers/AnthropicRateLimitRequestsRemaining' + anthropic-ratelimit-requests-reset: + $ref: '#/components/headers/AnthropicRateLimitRequestsReset' + anthropic-ratelimit-tokens-limit: + $ref: '#/components/headers/AnthropicRateLimitTokensLimit' + anthropic-ratelimit-tokens-remaining: + $ref: '#/components/headers/AnthropicRateLimitTokensRemaining' + anthropic-ratelimit-tokens-reset: + $ref: '#/components/headers/AnthropicRateLimitTokensReset' + anthropic-ratelimit-input-tokens-limit: + $ref: '#/components/headers/AnthropicRateLimitInputTokensLimit' + anthropic-ratelimit-input-tokens-remaining: + $ref: '#/components/headers/AnthropicRateLimitInputTokensRemaining' + anthropic-ratelimit-input-tokens-reset: + $ref: '#/components/headers/AnthropicRateLimitInputTokensReset' + anthropic-ratelimit-output-tokens-limit: + $ref: '#/components/headers/AnthropicRateLimitOutputTokensLimit' + anthropic-ratelimit-output-tokens-remaining: + $ref: '#/components/headers/AnthropicRateLimitOutputTokensRemaining' + anthropic-ratelimit-output-tokens-reset: + $ref: '#/components/headers/AnthropicRateLimitOutputTokensReset' + anthropic-priority-input-tokens-limit: + $ref: '#/components/headers/AnthropicPriorityInputTokensLimit' + anthropic-priority-input-tokens-remaining: + $ref: '#/components/headers/AnthropicPriorityInputTokensRemaining' + anthropic-priority-input-tokens-reset: + $ref: '#/components/headers/AnthropicPriorityInputTokensReset' + anthropic-priority-output-tokens-limit: + $ref: '#/components/headers/AnthropicPriorityOutputTokensLimit' + anthropic-priority-output-tokens-remaining: + $ref: '#/components/headers/AnthropicPriorityOutputTokensRemaining' + anthropic-priority-output-tokens-reset: + $ref: '#/components/headers/AnthropicPriorityOutputTokensReset' + content: + application/json: + schema: + $ref: '#/components/schemas/MessageResponse' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + /v1/messages/count_tokens: + post: + tags: + - Messages + summary: Count tokens for a chat completion + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/AnthropicBetaHeader' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - messages + - model + properties: + messages: + $ref: '#/components/schemas/MessageRequest' + model: + $ref: '#/components/schemas/ModelRequest' + mcp_servers: + $ref: '#/components/schemas/MCPServers' + system: + $ref: '#/components/schemas/SystemRequest' + thinking: + $ref: '#/components/schemas/ThinkingRequest' + tool_choice: + $ref: '#/components/schemas/ToolChoiceRequest' + tools: + $ref: '#/components/schemas/ToolsRequest' + responses: + '200': + description: Successful response + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + type: object + required: + - input_tokens + properties: + input_tokens: + type: integer + description: The total number of tokens across the provided list of messages, system prompt, and tools. + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + /v1/models: + get: + tags: + - Models + summary: List Models + description: 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. + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/BeforeId' + - $ref: '#/components/parameters/AfterId' + - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + responses: + '200': + description: Successfully retrieved models + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + type: object + required: + - data + - first_id + - has_more + - last_id + properties: + data: + type: array + items: + $ref: '#/components/schemas/ModelResponseData' + first_id: + $ref: '#/components/schemas/FirstId' + has_more: + $ref: '#/components/schemas/HasMore' + last_id: + $ref: '#/components/schemas/LastId' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + /v1/models/{model_id}: + get: + tags: + - Models + summary: Get a Model + description: 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. + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + - name: model_id + in: path + required: true + schema: + type: string + description: Model identifier or alias. + responses: + '200': + description: Successfully retrieved model + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + $ref: '#/components/schemas/ModelResponseData' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + /v1/messages/batches: + post: + tags: + - Message Batches + - text-generation + summary: Create multiple chat completions in a batch + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/AnthropicBetaHeader' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - requests + properties: + requests: + type: array + items: + type: object + required: + - custom_id + - params + properties: + custom_id: + type: string + minLength: 1 + maxLength: 64 + params: + $ref: '#/components/schemas/MessageRequestBody' + responses: + '200': + description: Successfully created message batch + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + anthropic-ratelimit-requests-limit: + $ref: '#/components/headers/AnthropicRateLimitRequestsLimit' + anthropic-ratelimit-requests-remaining: + $ref: '#/components/headers/AnthropicRateLimitRequestsRemaining' + anthropic-ratelimit-requests-reset: + $ref: '#/components/headers/AnthropicRateLimitRequestsReset' + anthropic-ratelimit-tokens-limit: + $ref: '#/components/headers/AnthropicRateLimitTokensLimit' + anthropic-ratelimit-tokens-remaining: + $ref: '#/components/headers/AnthropicRateLimitTokensRemaining' + anthropic-ratelimit-tokens-reset: + $ref: '#/components/headers/AnthropicRateLimitTokensReset' + anthropic-ratelimit-input-tokens-limit: + $ref: '#/components/headers/AnthropicRateLimitInputTokensLimit' + anthropic-ratelimit-input-tokens-remaining: + $ref: '#/components/headers/AnthropicRateLimitInputTokensRemaining' + anthropic-ratelimit-input-tokens-reset: + $ref: '#/components/headers/AnthropicRateLimitInputTokensReset' + anthropic-ratelimit-output-tokens-limit: + $ref: '#/components/headers/AnthropicRateLimitOutputTokensLimit' + anthropic-ratelimit-output-tokens-remaining: + $ref: '#/components/headers/AnthropicRateLimitOutputTokensRemaining' + anthropic-ratelimit-output-tokens-reset: + $ref: '#/components/headers/AnthropicRateLimitOutputTokensReset' + anthropic-priority-input-tokens-limit: + $ref: '#/components/headers/AnthropicPriorityInputTokensLimit' + anthropic-priority-input-tokens-remaining: + $ref: '#/components/headers/AnthropicPriorityInputTokensRemaining' + anthropic-priority-input-tokens-reset: + $ref: '#/components/headers/AnthropicPriorityInputTokensReset' + anthropic-priority-output-tokens-limit: + $ref: '#/components/headers/AnthropicPriorityOutputTokensLimit' + anthropic-priority-output-tokens-remaining: + $ref: '#/components/headers/AnthropicPriorityOutputTokensRemaining' + anthropic-priority-output-tokens-reset: + $ref: '#/components/headers/AnthropicPriorityOutputTokensReset' + content: + application/json: + schema: + $ref: '#/components/schemas/MessageBatchResponse' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + get: + tags: + - Message Batches + summary: List Message Batches + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/BeforeId' + - $ref: '#/components/parameters/AfterId' + - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + responses: + '200': + description: Successfully retrieved message batches + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + type: object + required: + - data + - first_id + - has_more + - last_id + properties: + data: + type: array + items: + $ref: '#/components/schemas/MessageBatchResponse' + has_more: + type: boolean + description: Indicates if there are more results in the requested page direction. + first_id: + type: string + nullable: true + description: First ID in the data list. Can be used as the before_id for the previous page. + last_id: + type: string + nullable: true + description: Last ID in the data list. Can be used as the after_id for the next page. + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + /v1/messages/batches/{message_batch_id}: + get: + tags: + - Message Batches + summary: Retrieve a Message Batch + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/BatchId' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + responses: + '200': + description: Successfully retrieved message batch + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + $ref: '#/components/schemas/MessageBatchResponse' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + delete: + tags: + - Message Batches + summary: Delete a Message Batch + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/BatchId' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + responses: + '200': + description: Successfully deleted message batch + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + type: object + required: + - id + - type + properties: + id: + type: string + type: + type: string + enum: [message_batch_deleted] + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + /v1/messages/batches/{message_batch_id}/results: + get: + tags: + - Message Batches + summary: Retrieve Message Batch Results. The path for retrieving Message Batch results should be pulled from the batch's results_url. This path should not be assumed and may change. + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/BatchId' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + responses: + '200': + description: Successfully retrieved batch results + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/x-jsonl: + schema: + required: + - custom_id + - result + properties: + custom_id: + type: string + description: 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: + type: object + oneOf: + - $ref: '#/components/schemas/MessageBatchSucceededResult' + - $ref: '#/components/schemas/MessageBatchErroredResult' + - $ref: '#/components/schemas/MessageBatchCanceledResult' + - $ref: '#/components/schemas/MessageBatchExpiredResult' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + /v1/messages/batches/{message_batch_id}/cancel: + post: + tags: + - Message Batches + summary: Cancel a Message Batch + description: Cancels an in-progress message batch. Non-interruptible requests may complete before cancellation is finalized. + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/BatchId' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + responses: + '200': + description: Successfully initiated batch cancellation + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + $ref: '#/components/schemas/MessageBatchResponse' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + /v1/files: # Currently in beta, Beta headers will be removed in the future + post: + tags: + - Files + summary: Upload a File + description: The Files API allows you to upload and manage files to use with the Anthropic API without having to re-upload content with each request. + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/AnthropicBetaHeader' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + required: + - file + properties: + file: + type: string + format: binary + description: The file to upload + responses: + '200': + description: Users retrieved successfully + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + $ref: '#/components/schemas/FileData' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + get: + tags: + - Files + summary: List Files + description: Retrieves a list of all files + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/AnthropicBetaHeader' + - $ref: '#/components/parameters/BeforeId' + - $ref: '#/components/parameters/AfterId' + - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + responses: + '200': + description: Files retrieved successfully + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + type: object + required: + - data + properties: + data: + type: array + items: + $ref: '#/components/schemas/FileData' + first_id: + $ref: '#/components/schemas/FirstId' + has_more: + $ref: '#/components/schemas/HasMore' + last_id: + $ref: '#/components/schemas/LastId' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + /v1/files/{file_id}: + get: + tags: + - Files + summary: Get a File + description: Retrieves a file by its ID + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/AnthropicBetaHeader' + - $ref: '#/components/parameters/FileId' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + responses: + '200': + description: File retrieved successfully + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + $ref: '#/components/schemas/FileData' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + delete: + tags: + - Files + summary: Delete a File + description: Deletes a file by its ID + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/AnthropicBetaHeader' + - $ref: '#/components/parameters/FileId' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + responses: + '200': + description: File deleted successfully + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + type: object + required: + - id + properties: + id: + type: string + description: ID of the deleted File. + type: + type: string + enum: ["file_deleted"] + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + /v1/files/{file_id}/content: + get: + tags: + - Files + summary: Get a File Content + description: Retrieves a file content by its ID + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/AnthropicBetaHeader' + - $ref: '#/components/parameters/FileId' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + responses: + '200': + description: File content retrieved successfully + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/octet-stream: + schema: + type: string + format: binary + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + /v1/organizations/users: + get: + tags: + - Admin - Organization Member Management + summary: List Users + description: Retrieves a list of all users + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/BeforeId' + - $ref: '#/components/parameters/AfterId' + - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + - name: email + in: query + schema: + type: string + description: Filter by user email. + responses: + '200': + description: Users retrieved successfully + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + type: object + required: + - data + - first_id + - has_more + - last_id + properties: + data: + type: array + items: + $ref: '#/components/schemas/AdminMemberManagementUserData' + first_id: + $ref: '#/components/schemas/FirstId' + has_more: + $ref: '#/components/schemas/HasMore' + last_id: + $ref: '#/components/schemas/LastId' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + /v1/organizations/users/{user_id}: + get: + tags: + - Admin - Organization Member Management + summary: Get User + description: Retrieves details for a specific user + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/UserId' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + responses: + '200': + description: User details retrieved successfully + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + $ref: '#/components/schemas/AdminMemberManagementUserData' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + post: + tags: + - Admin - Organization Member Management + summary: Update User + description: Updates details for a specific user + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/UserId' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - role + properties: + role: + type: string + enum: [user, developer, billing] + description: New role for the User. Cannot be "admin". + responses: + '200': + description: User updated successfully + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + $ref: '#/components/schemas/AdminMemberManagementUserData' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + delete: + tags: + - Admin - Organization Member Management + summary: Remove User + description: Removes a user from the organization + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/UserId' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + responses: + '200': + description: User successfully removed + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + type: object + required: + - id + - type + properties: + id: + type: string + description: ID of the deleted User. + type: + type: string + enum: [user_deleted] + default: user_deleted + description: Object type. For deleted Users, this is always "user_deleted". + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + /v1/organizations/invites: + get: + tags: + - Admin - Organization Invites + summary: List Invites + description: Retrieves a list of all organization invites + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/BeforeId' + - $ref: '#/components/parameters/AfterId' + - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + responses: + '200': + description: Invites retrieved successfully + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + type: object + required: + - data + - first_id + - has_more + - last_id + properties: + data: + type: array + items: + $ref: '#/components/schemas/AdminInviteData' + first_id: + $ref: '#/components/schemas/FirstId' + has_more: + $ref: '#/components/schemas/HasMore' + last_id: + $ref: '#/components/schemas/LastId' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + post: + tags: + - Admin - Organization Invites + summary: Create Invite + description: Creates a new organization invite + security: + - ApiKeyAuth: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - email + - role + properties: + email: + type: string + description: Email of the User. + role: + type: string + enum: [user, developer, billing] + description: Role for the invited User. Cannot be "admin". + responses: + '200': + description: Invite created successfully + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + $ref: '#/components/schemas/AdminInviteData' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + /v1/organizations/invites/{invite_id}: + get: + tags: + - Admin - Organization Invites + summary: Get Invite + description: Retrieves details for a specific invite + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/InviteId' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + responses: + '200': + description: Invite details retrieved successfully + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + $ref: '#/components/schemas/AdminInviteData' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + delete: + tags: + - Admin - Organization Invites + summary: Delete Invite + description: Deletes an existing organization invite + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/InviteId' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + responses: + '200': + description: Invite deleted successfully + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + type: object + required: + - id + - type + properties: + id: + type: string + description: ID of the deleted Invite. + type: + type: string + enum: [invite_deleted] + default: invite_deleted + description: Deleted object type. For Invites, this is always "invite_deleted". + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + /v1/organizations/workspaces: + get: + tags: + - Admin - Workspace Management + summary: List Workspaces + description: Retrieves a list of all workspaces + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - name: include_archived + in: query + schema: + type: boolean + default: false + description: Whether to include Workspaces that have been archived in the response + - $ref: '#/components/parameters/BeforeId' + - $ref: '#/components/parameters/AfterId' + - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + responses: + '200': + description: Workspaces retrieved successfully + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + type: object + required: + - data + - first_id + - has_more + - last_id + properties: + data: + type: array + items: + $ref: '#/components/schemas/AdminWorkspaceData' + first_id: + $ref: '#/components/schemas/FirstId' + has_more: + $ref: '#/components/schemas/HasMore' + last_id: + $ref: '#/components/schemas/LastId' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + post: + tags: + - Admin - Workspace Management + summary: Create Workspace + description: Creates a new workspace + security: + - ApiKeyAuth: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - name + properties: + name: + type: string + minLength: 1 + maxLength: 40 + description: Name of the Workspace. + responses: + '200': + description: Workspace created successfully + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + $ref: '#/components/schemas/AdminWorkspaceData' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + /v1/organizations/workspaces/{workspace_id}: + get: + tags: + - Admin - Workspace Management + summary: Get Workspace + description: Retrieves details for a specific workspace + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/WorkspaceId' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + responses: + '200': + description: Workspace details retrieved successfully + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + $ref: '#/components/schemas/AdminWorkspaceData' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + post: + tags: + - Admin - Workspace Management + summary: Update Workspace + description: Updates details for a specific workspace + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/WorkspaceId' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - name + properties: + name: + type: string + minLength: 1 + maxLength: 40 + description: Name of the Workspace. + responses: + '200': + description: Workspace updated successfully + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + $ref: '#/components/schemas/AdminWorkspaceData' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + /v1/organizations/workspaces/{workspace_id}/archive: + post: + tags: + - Admin - Workspace Management + summary: Archive Workspace + description: Archives a specific workspace + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/WorkspaceId' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + responses: + '200': + description: Workspace archived successfully + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + $ref: '#/components/schemas/AdminWorkspaceData' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + /v1/organizations/workspaces/{workspace_id}/members: + get: + tags: + - Admin - Workspace Member Management + summary: List Workspace Members + description: Retrieves a list of all members in a workspace + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/WorkspaceId' + - $ref: '#/components/parameters/BeforeId' + - $ref: '#/components/parameters/AfterId' + - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + responses: + '200': + description: Workspace members retrieved successfully + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + type: object + required: + - data + - first_id + - has_more + - last_id + properties: + data: + type: array + items: + $ref: '#/components/schemas/AdminWorkspaceMemberData' + first_id: + $ref: '#/components/schemas/FirstId' + has_more: + $ref: '#/components/schemas/HasMore' + last_id: + $ref: '#/components/schemas/LastId' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + post: + tags: + - Admin - Workspace Member Management + summary: Create Workspace Member + description: Creates a new workspace member + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/WorkspaceId' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - user_id + - workspace_role + properties: + user_id: + type: string + description: ID of the User. + workspace_role: + type: string + enum: [workspace_user, workspace_developer, workspace_admin] + description: Role of the new Workspace Member. Cannot be "workspace_billing". + responses: + '200': + description: Workspace member created successfully + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + $ref: '#/components/schemas/AdminWorkspaceMemberData' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + /v1/organizations/workspaces/{workspace_id}/members/{user_id}: + get: + tags: + - Admin - Workspace Member Management + summary: Get Workspace Member + description: Retrieves details for a specific workspace member + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/WorkspaceId' + - $ref: '#/components/parameters/UserId' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + responses: + '200': + description: Workspace member details retrieved successfully + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + $ref: '#/components/schemas/AdminWorkspaceMemberData' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + post: + tags: + - Admin - Workspace Member Management + summary: Update Workspace Member + description: Updates details for a specific workspace member + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/WorkspaceId' + - $ref: '#/components/parameters/UserId' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - workspace_role + properties: + workspace_role: + type: string + enum: [workspace_user, workspace_developer, workspace_admin, workspace_billing] + description: New workspace role for the User. + responses: + '200': + description: Workspace member updated successfully + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + $ref: '#/components/schemas/AdminWorkspaceMemberData' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + delete: + tags: + - Admin - Workspace Member Management + summary: Delete Workspace Member + description: Removes a member from the workspace + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/WorkspaceId' + - $ref: '#/components/parameters/UserId' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + responses: + '200': + description: Workspace member successfully removed + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + type: object + required: + - type + - user_id + - workspace_id + properties: + type: + type: string + enum: [workspace_member_deleted] + default: workspace_member_deleted + description: Deleted object type. For Workspace Members, this is always "workspace_member_deleted". + user_id: + type: string + description: ID of the User. + workspace_id: + type: string + description: ID of the Workspace. + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + /v1/organizations/api_keys: + get: + tags: + - Admin - API Keys + summary: List Api Keys + description: Retrieves a list of all API keys + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/BeforeId' + - $ref: '#/components/parameters/AfterId' + - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + - name: status + in: query + schema: + type: string + nullable: true + enum: [active, inactive, archived] + description: Filter by API key status. + - name: workspace_id + in: query + schema: + type: string + nullable: true + description: Filter by Workspace ID. + - name: created_by_user_id + in: query + schema: + type: string + nullable: true + description: Filter by the ID of the User who created the object. + responses: + '200': + description: API keys retrieved successfully + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + type: object + required: + - data + - first_id + - has_more + - last_id + properties: + data: + type: array + items: + $ref: '#/components/schemas/AdminAPIKeyData' + first_id: + $ref: '#/components/schemas/FirstId' + has_more: + $ref: '#/components/schemas/HasMore' + last_id: + $ref: '#/components/schemas/LastId' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + /v1/organizations/api_keys/{api_key_id}: + get: + tags: + - Admin - API Keys + summary: Get Api Key + description: Retrieves details for a specific API key + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/ApiKeyId' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + responses: + '200': + description: API key details retrieved successfully + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + $ref: '#/components/schemas/AdminAPIKeyData' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + post: + tags: + - Admin - API Keys + summary: Update Api Key + description: Updates details for a specific API key + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/ApiKeyId' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + name: + type: string + minLength: 1 + maxLength: 500 + description: Name of the API key. + status: + type: string + nullable: true + enum: [active, inactive, archived] + description: Status of the API key. + responses: + '200': + description: API key updated successfully + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + $ref: '#/components/schemas/AdminAPIKeyData' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + /v1/experimental/generate_prompt: + post: + tags: + - Experimental - Prompt Tools + summary: Generate Prompt + description: Generates a prompt for a given input + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicBetaHeader' + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - task + properties: + task: + type: string + description: Description of the prompt's purpose. + target_model: + type: string + nullable: true + description: The model this prompt will be used for. + minLength: 1 + maxLength: 256 + responses: + '200': + description: Successful response + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + anthropic-ratelimit-requests-limit: + $ref: '#/components/headers/AnthropicRateLimitRequestsLimit' + anthropic-ratelimit-requests-remaining: + $ref: '#/components/headers/AnthropicRateLimitRequestsRemaining' + anthropic-ratelimit-requests-reset: + $ref: '#/components/headers/AnthropicRateLimitRequestsReset' + anthropic-ratelimit-tokens-limit: + $ref: '#/components/headers/AnthropicRateLimitTokensLimit' + anthropic-ratelimit-tokens-remaining: + $ref: '#/components/headers/AnthropicRateLimitTokensRemaining' + anthropic-ratelimit-tokens-reset: + $ref: '#/components/headers/AnthropicRateLimitTokensReset' + anthropic-ratelimit-input-tokens-limit: + $ref: '#/components/headers/AnthropicRateLimitInputTokensLimit' + anthropic-ratelimit-input-tokens-remaining: + $ref: '#/components/headers/AnthropicRateLimitInputTokensRemaining' + anthropic-ratelimit-input-tokens-reset: + $ref: '#/components/headers/AnthropicRateLimitInputTokensReset' + anthropic-ratelimit-output-tokens-limit: + $ref: '#/components/headers/AnthropicRateLimitOutputTokensLimit' + anthropic-ratelimit-output-tokens-remaining: + $ref: '#/components/headers/AnthropicRateLimitOutputTokensRemaining' + anthropic-ratelimit-output-tokens-reset: + $ref: '#/components/headers/AnthropicRateLimitOutputTokensReset' + anthropic-priority-input-tokens-limit: + $ref: '#/components/headers/AnthropicPriorityInputTokensLimit' + anthropic-priority-input-tokens-remaining: + $ref: '#/components/headers/AnthropicPriorityInputTokensRemaining' + anthropic-priority-input-tokens-reset: + $ref: '#/components/headers/AnthropicPriorityInputTokensReset' + anthropic-priority-output-tokens-limit: + $ref: '#/components/headers/AnthropicPriorityOutputTokensLimit' + anthropic-priority-output-tokens-remaining: + $ref: '#/components/headers/AnthropicPriorityOutputTokensRemaining' + anthropic-priority-output-tokens-reset: + $ref: '#/components/headers/AnthropicPriorityOutputTokensReset' + content: + application/json: + schema: + type: object + required: + - messages + - system + - usage + properties: + messages: + type: array + description: The response contains a list of message objects in the same format used by the Messages API. Typically includes a user message with the complete generated prompt text, and may include an assistant message with a prefill to guide the model's initial response. These messages can be used directly in a Messages API request to start a conversation with the generated prompt. + items: + $ref: '#/components/schemas/Message' + system: + type: string + default: "" + description: Currently, the system field is always returned as an empty string (""). In future iterations, this field may contain generated system prompts. Directions similar to what would normally be included in a system prompt are included in messages when generating a prompt. + usage: + $ref: '#/components/schemas/Usage' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + /v1/experimental/improve_prompt: + post: + tags: + - Experimental - Prompt Tools + summary: Improve Prompt + description: Improves a prompt for a given input + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicBetaHeader' + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - messages + properties: + messages: + type: array + description: The response contains a list of message objects in the same format used by the Messages API. Typically includes a user message with the complete generated prompt text, and may include an assistant message with a prefill to guide the model's initial response. These messages can be used directly in a Messages API request to start a conversation with the generated prompt. + items: + $ref: '#/components/schemas/Message' + feedback: + type: string + nullable: true + system: + type: string + nullable: true + target_model: + type: string + nullable: true + description: The model this prompt will be used for. + minLength: 1 + maxLength: 256 + responses: + '200': + description: Successful response + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + anthropic-ratelimit-requests-limit: + $ref: '#/components/headers/AnthropicRateLimitRequestsLimit' + anthropic-ratelimit-requests-remaining: + $ref: '#/components/headers/AnthropicRateLimitRequestsRemaining' + anthropic-ratelimit-requests-reset: + $ref: '#/components/headers/AnthropicRateLimitRequestsReset' + anthropic-ratelimit-tokens-limit: + $ref: '#/components/headers/AnthropicRateLimitTokensLimit' + anthropic-ratelimit-tokens-remaining: + $ref: '#/components/headers/AnthropicRateLimitTokensRemaining' + anthropic-ratelimit-tokens-reset: + $ref: '#/components/headers/AnthropicRateLimitTokensReset' + anthropic-ratelimit-input-tokens-limit: + $ref: '#/components/headers/AnthropicRateLimitInputTokensLimit' + anthropic-ratelimit-input-tokens-remaining: + $ref: '#/components/headers/AnthropicRateLimitInputTokensRemaining' + anthropic-ratelimit-input-tokens-reset: + $ref: '#/components/headers/AnthropicRateLimitInputTokensReset' + anthropic-ratelimit-output-tokens-limit: + $ref: '#/components/headers/AnthropicRateLimitOutputTokensLimit' + anthropic-ratelimit-output-tokens-remaining: + $ref: '#/components/headers/AnthropicRateLimitOutputTokensRemaining' + anthropic-ratelimit-output-tokens-reset: + $ref: '#/components/headers/AnthropicRateLimitOutputTokensReset' + anthropic-priority-input-tokens-limit: + $ref: '#/components/headers/AnthropicPriorityInputTokensLimit' + anthropic-priority-input-tokens-remaining: + $ref: '#/components/headers/AnthropicPriorityInputTokensRemaining' + anthropic-priority-input-tokens-reset: + $ref: '#/components/headers/AnthropicPriorityInputTokensReset' + anthropic-priority-output-tokens-limit: + $ref: '#/components/headers/AnthropicPriorityOutputTokensLimit' + anthropic-priority-output-tokens-remaining: + $ref: '#/components/headers/AnthropicPriorityOutputTokensRemaining' + anthropic-priority-output-tokens-reset: + $ref: '#/components/headers/AnthropicPriorityOutputTokensReset' + content: + application/json: + schema: + type: object + required: + - messages + - system + - usage + properties: + messages: + type: array + description: The response contains a list of message objects in the same format used by the Messages API. Typically includes a user message with the complete generated prompt text, and may include an assistant message with a prefill to guide the model's initial response. These messages can be used directly in a Messages API request to start a conversation with the generated prompt. + items: + $ref: '#/components/schemas/Message' + system: + type: string + default: "" + description: Currently, the system field is always returned as an empty string (""). In future iterations, this field may contain generated system prompts. Directions similar to what would normally be included in a system prompt are included in messages when generating a prompt. + usage: + $ref: '#/components/schemas/Usage' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + /v1/experimental/templatize_prompt: + post: + tags: + - Experimental - Prompt Tools + summary: Templatize Prompt + description: Templatizes a prompt for a given input + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicBetaHeader' + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - messages + properties: + messages: + type: array + description: The response contains a list of message objects in the same format used by the Messages API. Typically includes a user message with the complete generated prompt text, and may include an assistant message with a prefill to guide the model's initial response. These messages can be used directly in a Messages API request to start a conversation with the generated prompt. + items: + $ref: '#/components/schemas/Message' + system: + type: string + nullable: true + responses: + '200': + description: Successful response + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + anthropic-ratelimit-requests-limit: + $ref: '#/components/headers/AnthropicRateLimitRequestsLimit' + anthropic-ratelimit-requests-remaining: + $ref: '#/components/headers/AnthropicRateLimitRequestsRemaining' + anthropic-ratelimit-requests-reset: + $ref: '#/components/headers/AnthropicRateLimitRequestsReset' + anthropic-ratelimit-tokens-limit: + $ref: '#/components/headers/AnthropicRateLimitTokensLimit' + anthropic-ratelimit-tokens-remaining: + $ref: '#/components/headers/AnthropicRateLimitTokensRemaining' + anthropic-ratelimit-tokens-reset: + $ref: '#/components/headers/AnthropicRateLimitTokensReset' + anthropic-ratelimit-input-tokens-limit: + $ref: '#/components/headers/AnthropicRateLimitInputTokensLimit' + anthropic-ratelimit-input-tokens-remaining: + $ref: '#/components/headers/AnthropicRateLimitInputTokensRemaining' + anthropic-ratelimit-input-tokens-reset: + $ref: '#/components/headers/AnthropicRateLimitInputTokensReset' + anthropic-ratelimit-output-tokens-limit: + $ref: '#/components/headers/AnthropicRateLimitOutputTokensLimit' + anthropic-ratelimit-output-tokens-remaining: + $ref: '#/components/headers/AnthropicRateLimitOutputTokensRemaining' + anthropic-ratelimit-output-tokens-reset: + $ref: '#/components/headers/AnthropicRateLimitOutputTokensReset' + anthropic-priority-input-tokens-limit: + $ref: '#/components/headers/AnthropicPriorityInputTokensLimit' + anthropic-priority-input-tokens-remaining: + $ref: '#/components/headers/AnthropicPriorityInputTokensRemaining' + anthropic-priority-input-tokens-reset: + $ref: '#/components/headers/AnthropicPriorityInputTokensReset' + anthropic-priority-output-tokens-limit: + $ref: '#/components/headers/AnthropicPriorityOutputTokensLimit' + anthropic-priority-output-tokens-remaining: + $ref: '#/components/headers/AnthropicPriorityOutputTokensRemaining' + anthropic-priority-output-tokens-reset: + $ref: '#/components/headers/AnthropicPriorityOutputTokensReset' + content: + application/json: + schema: + type: object + required: + - messages + - system + - usage + - variable_values + properties: + messages: + type: array + description: The response contains a list of message objects in the same format used by the Messages API. Typically includes a user message with the complete generated prompt text, and may include an assistant message with a prefill to guide the model's initial response. These messages can be used directly in a Messages API request to start a conversation with the generated prompt. + items: + $ref: '#/components/schemas/Message' + system: + type: string + default: "" + description: Currently, the system field is always returned as an empty string (""). In future iterations, this field may contain generated system prompts. Directions similar to what would normally be included in a system prompt are included in messages when generating a prompt. + usage: + $ref: '#/components/schemas/Usage' + variable_values: + type: object + description: A mapping of template variable names to their original values, as extracted from the input prompt during templatization. Each key represents a variable name identified in the templatized prompt, and each value contains the corresponding content from the original prompt that was replaced by that variable. + additionalProperties: + type: string + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + \ No newline at end of file diff --git a/tests/ai-workspace-cli-e2e/resources/llm-provider/edit/metadata.yaml b/tests/ai-workspace-cli-e2e/resources/llm-provider/edit/metadata.yaml new file mode 100644 index 000000000..80160b15e --- /dev/null +++ b/tests/ai-workspace-cli-e2e/resources/llm-provider/edit/metadata.yaml @@ -0,0 +1,16 @@ +apiVersion: ai-workspace.api-platform.wso2.com/v1alpha +kind: LlmProvider +metadata: + name: claude-provider +spec: + displayName: wso2 claude provider + version: v1.0 + # associatedGateways must live under spec for the CLI to fold it into the + # payload. These gateway handles are registered by the suite before apply. + associatedGateways: + - id: prod-eu-01 + configurations: + host: prod-eu-01.api.wso2.com + - id: prod-eu-02 + configurations: + host: prod-eu-02.api.wso2.com diff --git a/tests/ai-workspace-cli-e2e/resources/llm-provider/edit/runtime.yaml b/tests/ai-workspace-cli-e2e/resources/llm-provider/edit/runtime.yaml new file mode 100644 index 000000000..6659dd408 --- /dev/null +++ b/tests/ai-workspace-cli-e2e/resources/llm-provider/edit/runtime.yaml @@ -0,0 +1,97 @@ +apiVersion: gateway.api-platform.wso2.com/v1 +kind: LlmProvider +metadata: + name: claude-provider +spec: + displayName: wso2 claude provider + version: v1.0 + context: /wso2-claude + template: anthropic + upstream: + url: https://api.anthropic.com + auth: + header: x-api-key + type: api-key + value: "11111" + accessControl: + mode: allow_all + policies: + - name: api-key-auth + paths: + - methods: + - '*' + params: + in: header + key: X-API-Key + path: /* + version: "" + - name: token-based-ratelimit + paths: + - methods: + - '*' + params: + totalTokenLimits: + - count: 11 + duration: 2h + path: /* + version: "" + - name: advanced-ratelimit + paths: + - methods: + - '*' + params: + keyExtraction: + - type: apiname + quotas: + - limits: + - duration: 1h + limit: 12 + name: request-limit + path: /* + version: "" + - name: llm-cost-based-ratelimit + paths: + - methods: + - '*' + params: + budgetLimits: + - amount: 3 + duration: 1h + path: /* + version: "" + - name: llm-cost + paths: + - methods: + - '*' + params: {} + path: /* + version: "" + - name: advanced-ratelimit + paths: + - methods: + - '*' + params: + quotas: + - keyExtraction: + - type: routename + - key: x-wso2-application-id + type: metadata + limits: + - duration: 2h + limit: 100 + name: consumer-request-limit + path: /v1/experimental/improve_prompt + - methods: + - '*' + params: + quotas: + - keyExtraction: + - type: routename + - key: x-wso2-application-id + type: metadata + limits: + - duration: 1h + limit: 1 + name: consumer-request-limit + path: /v1/files + version: "" diff --git a/tests/ai-workspace-cli-e2e/resources/llm-proxy/create/metadata.yaml b/tests/ai-workspace-cli-e2e/resources/llm-proxy/create/metadata.yaml new file mode 100644 index 000000000..6e087c2b4 --- /dev/null +++ b/tests/ai-workspace-cli-e2e/resources/llm-proxy/create/metadata.yaml @@ -0,0 +1,7 @@ +apiVersion: ai-workspace.api-platform.wso2.com/v1alpha +kind: LlmProxy +metadata: + name: claude-proxy +spec: + displayName: claude Proxy + version: v1.0 diff --git a/tests/ai-workspace-cli-e2e/resources/llm-proxy/create/runtime.yaml b/tests/ai-workspace-cli-e2e/resources/llm-proxy/create/runtime.yaml new file mode 100644 index 000000000..eb89131e5 --- /dev/null +++ b/tests/ai-workspace-cli-e2e/resources/llm-proxy/create/runtime.yaml @@ -0,0 +1,73 @@ +apiVersion: gateway.api-platform.wso2.com/v1 +kind: LlmProxy +metadata: + name: claude-proxy +spec: + displayName: wso2 claude proxy + version: v1.0 + context: /default/claude-proxy + provider: + id: claude-provider + auth: + header: X-API-Key + type: api-key + value: '{{ secret "13b216e7-10d9-47ca-b343-e0a546ab718b" }}' + globalPolicies: + - name: api-key-auth + params: + in: header + key: X-API-Key + version: "" + operationPolicies: + - name: basic-auth + paths: + - methods: + - POST + params: + allowUnauthenticated: false + password: admin + realm: Restricted + username: admin + path: /v1/messages + version: v1 + - name: cors + paths: + - methods: + - POST + params: + allowCredentials: false + allowedHeaders: [] + allowedMethods: + - GET + - POST + - PUT + - DELETE + - OPTIONS + allowedOrigins: + - '*' + exposedHeaders: [] + forwardPreflight: false + maxAge: 3600 + path: /v1/messages + version: v1 + - name: content-length-guardrail + paths: + - methods: + - POST + params: + request: + enabled: true + invert: false + jsonPath: $.messages[-1].content + max: 0 + min: 0 + showAssessment: false + response: + enabled: false + invert: false + jsonPath: $.choices[0].message.content + max: 0 + min: 0 + showAssessment: false + path: /v1/messages + version: v1 diff --git a/tests/ai-workspace-cli-e2e/resources/llm-proxy/definition.yaml b/tests/ai-workspace-cli-e2e/resources/llm-proxy/definition.yaml new file mode 100644 index 000000000..2fea2df81 --- /dev/null +++ b/tests/ai-workspace-cli-e2e/resources/llm-proxy/definition.yaml @@ -0,0 +1,2734 @@ +openapi: 3.0.0 +info: + title: Anthropic Claude API + version: 1.0.0 + description: | + The Anthropic Claude API provides access to Claude, a family of large language models capable of performing a wide range of natural language processing tasks including chat completion, tool use, code execution, and document analysis. + The API supports both real-time and batch interactions, with advanced configuration options for tool usage, streaming, caching, and system-level reasoning. + x-marketplace-tags: + - name: AI + description: AI and LLM APIs + +servers: + - url: https://api.anthropic.com + +components: + securitySchemes: + ApiKeyAuth: + type: apiKey + in: header + name: x-api-key + description: Your unique API key for authentication. Required in the header of all API requests to authenticate your account and access Anthropic's services. Get your API key through the Console. Each key is scoped to a Workspace. + + parameters: + AnthropicVersionHeader: + in: header + name: anthropic-version + description: The version of the Anthropic API you want to use. Required header for all API requests. Must be set to "2023-06-01". + required: true + schema: + type: string + enum: + - "2023-06-01" + + AnthropicBetaHeader: + in: header + name: anthropic-beta + description: Optional header to specify beta feature(s) you want to use. Multiple betas can be specified using a comma-separated list or by including the header multiple times. + required: false + schema: + type: array + items: + type: string + enum: + - "computer-use-2024-10-22" + - "computer-use-2025-01-24" + - "output-128k-2025-02-19" + - "token-efficient-tools-2025-02-19" + - "prompt-tools-2025-04-02" + - "interleaved-thinking-2025-05-14" + - "files-api-2025-04-14" + + AnthropicDangerousDirectBrowserAccessHeader: + in: header + name: anthropic-dangerous-direct-browser-access + description: This header must be set to "true" to enable direct client-side (browser) access to the API. This acknowledges the security risks of exposing your API key and is not recommended for production environments. + required: false + schema: + type: boolean + + BeforeId: + name: before_id + in: query + schema: + type: string + description: ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately before this object. + + AfterId: + name: after_id + in: query + schema: + type: string + description: ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately after this object. + + Limit: + name: limit + in: query + schema: + type: integer + default: 20 + minimum: 1 + maximum: 1000 + description: Number of items to return per page. + + BatchId: + name: message_batch_id + in: path + required: true + schema: + type: string + + FileId: + name: file_id + in: path + required: true + schema: + type: string + description: ID of the File. + + UserId: + name: user_id + in: path + required: true + schema: + type: string + description: ID of the User. + + InviteId: + name: invite_id + in: path + required: true + schema: + type: string + description: ID of the Invite. + + WorkspaceId: + name: workspace_id + in: path + required: true + schema: + type: string + description: ID of the Workspace. + + ApiKeyId: + name: api_key_id + in: path + required: true + schema: + type: string + description: ID of the API key. + + headers: + RequestId: + description: A unique identifier for the request. + schema: + type: string + + AnthropicOrganizationId: + description: The ID of the organization that the request belongs to. + schema: + type: string + + RetryAfter: + description: The number of seconds until you can retry the request. + schema: + type: integer + + AnthropicRateLimitRequestsLimit: + description: The maximum number of requests allowed within any rate limit period. + schema: + type: integer + + AnthropicRateLimitRequestsRemaining: + description: The number of requests remaining before being rate limited. + schema: + type: integer + + AnthropicRateLimitRequestsReset: + description: The time when the request rate limit will reset, provided in RFC 3339 format. + schema: + type: string + format: date-time + + AnthropicRateLimitTokensLimit: + description: The maximum number of tokens allowed within any rate limit period. + schema: + type: integer + + AnthropicRateLimitTokensRemaining: + description: The number of tokens remaining (rounded to the nearest thousand) before being rate limited. + schema: + type: integer + + AnthropicRateLimitTokensReset: + description: The time when the token rate limit will reset, provided in RFC 3339 format. + schema: + type: string + format: date-time + + AnthropicRateLimitInputTokensLimit: + description: The maximum number of input tokens allowed within any rate limit period. + schema: + type: integer + + AnthropicRateLimitInputTokensRemaining: + description: The number of input tokens remaining (rounded to the nearest thousand) before being rate limited. + schema: + type: integer + + AnthropicRateLimitInputTokensReset: + description: The time when the input token rate limit will reset, provided in RFC 3339 format. + schema: + type: string + format: date-time + + AnthropicRateLimitOutputTokensLimit: + description: The maximum number of output tokens allowed within any rate limit period. + schema: + type: integer + + AnthropicRateLimitOutputTokensRemaining: + description: The number of output tokens remaining (rounded to the nearest thousand) before being rate limited. + schema: + type: integer + + AnthropicRateLimitOutputTokensReset: + description: The time when the output token rate limit will reset, provided in RFC 3339 format. + schema: + type: string + format: date-time + + AnthropicPriorityInputTokensLimit: + description: The maximum number of Priority Tier input tokens allowed within any rate limit period. (Priority Tier only) + schema: + type: integer + + AnthropicPriorityInputTokensRemaining: + description: The number of Priority Tier input tokens remaining (rounded to the nearest thousand) before being rate limited. (Priority Tier only) + schema: + type: integer + + AnthropicPriorityInputTokensReset: + description: The time when the Priority Tier input token rate limit will be fully replenished, provided in RFC 3339 format. (Priority Tier only) + schema: + type: string + format: date-time + + AnthropicPriorityOutputTokensLimit: + description: The maximum number of Priority Tier output tokens allowed within any rate limit period. (Priority Tier only) + schema: + type: integer + + AnthropicPriorityOutputTokensRemaining: + description: The number of Priority Tier output tokens remaining (rounded to the nearest thousand) before being rate limited. (Priority Tier only) + schema: + type: integer + + AnthropicPriorityOutputTokensReset: + description: The time when the Priority Tier output token rate limit will be fully replenished, provided in RFC 3339 format. (Priority Tier only) + schema: + type: string + format: date-time + + schemas: + CacheControl: + type: object + nullable: true + required: + - type + properties: + type: + type: string + enum: [ephemeral] + ttl: + type: string + enum: [5m, 1h] + description: Controls caching behavior for this content + + CharacterLocation: + type: object + required: + - cited_text + - document_index + - document_title + - end_char_index + - start_char_index + - type + properties: + cited_text: + type: string + description: The text being cited + document_index: + type: integer + description: Should be greater than 0 + minimum: 1 + document_title: + type: string + nullable: true + minLength: 1 + maxLength: 256 + end_char_index: + type: integer + start_char_index: + type: integer + description: Should be greater than 0 + minimum: 1 + type: + type: string + enum: [char_location] + default: char_location + + PageLocation: + type: object + required: + - cited_text + - document_index + - document_title + - end_page_number + - start_page_number + - type + properties: + cited_text: + type: string + description: The text being cited + document_index: + type: integer + description: Should be greater than 0 + minimum: 1 + document_title: + type: string + nullable: true + minLength: 1 + maxLength: 256 + end_page_number: + type: integer + start_page_number: + type: integer + description: Should be greater than 0 + minimum: 1 + type: + type: string + enum: [page_location] + default: page_location + + ContentBlockLocation: + type: object + required: + - cited_text + - document_index + - document_title + - end_block_index + - start_block_index + - type + properties: + cited_text: + type: string + description: The text being cited + document_index: + type: integer + description: Should be greater than 0 + minimum: 1 + document_title: + type: string + nullable: true + minLength: 1 + maxLength: 256 + end_block_index: + type: integer + start_block_index: + type: integer + description: Should be greater than 0 + minimum: 1 + type: + type: string + enum: [content_block_location] + + RequestWebSearchResultLocationCitation: + type: object + required: + - cited_text + - encrypted_index + - title + - type + - url + properties: + cited_text: + type: string + encrypted_index: + type: string + title: + type: string + nullable: true + minLength: 1 + maxLength: 256 + type: + type: string + enum: [web_search_result_location] + url: + type: string + minLength: 1 + maxLength: 2048 + + Citations: + type: array + nullable: true + description: Citations supporting the text block. + items: + oneOf: + - $ref: '#/components/schemas/CharacterLocation' + - $ref: '#/components/schemas/PageLocation' + - $ref: '#/components/schemas/ContentBlockLocation' + - $ref: '#/components/schemas/RequestWebSearchResultLocationCitation' + + ServerToolUse: + type: object + required: + - id + - input + - name + - type + properties: + id: + type: string + input: + type: object + name: + type: string + enum: [web_search, code_execution] + cache_control: + $ref: '#/components/schemas/CacheControl' + + WebSearchToolResultObject: + type: object + required: + - error_code + - type + properties: + error_code: + type: string + enum: [invalid_tool_input, unavailable, max_uses_exceeded, too_many_requests, query_too_long] + type: + type: string + enum: [web_search_tool_result_error] + + WebSearchToolResultObjectArray: + type: array + items: + type: object + required: + - encrypted_content + - page_age + - title + - type + - url + properties: + encrypted_content: + type: string + page_age: + type: string + nullable: true + title: + type: string + minLength: 1 + type: + type: string + enum: [web_search_result] + url: + type: string + minLength: 1 + + WebSearchToolResult: + type: object + required: + - content + - tool_use_id + - type + properties: + content: + oneOf: + - $ref: '#/components/schemas/WebSearchToolResultObject' + - $ref: '#/components/schemas/WebSearchToolResultObjectArray' + tool_use_id: + type: string + type: + type: string + enum: [web_search_tool_result] + cache_control: + $ref: '#/components/schemas/CacheControl' + + RequestCodeExecutionToolResultBlockErrorContent: + type: object + required: + - error_code + - type + properties: + error_code: + type: string + enum: [invalid_tool_input, unavailable, max_uses_exceeded, too_many_requests, execution_time_exceeded] + type: + type: string + enum: [code_execution_tool_result_error] + + RequestCodeExecutionToolResultBlockContent: + type: object + required: + - content + - return_code + - stderr + - stdout + - type + properties: + content: + type: array + items: + type: object + required: + - file_id + - type + properties: + file_id: + type: string + type: + type: string + enum: [code_execution_output] + return_code: + type: integer + stderr: + type: string + stdout: + type: string + type: + type: string + enum: [code_execution_result] + + RequestCodeExecutionToolResultBlock: + type: object + required: + - content + - tool_use_id + - type + properties: + content: + type: object + oneOf: + - $ref: '#/components/schemas/RequestCodeExecutionToolResultBlockErrorContent' + - $ref: '#/components/schemas/RequestCodeExecutionToolResultBlockContent' + tool_use_id: + type: string + type: + type: string + enum: [code_execution_tool_result] + cache_control: + $ref: '#/components/schemas/CacheControl' + + RequestMCPToolUseBlock: + type: object + required: + - id + - input + - name + - server_name + - type + properties: + id: + type: string + input: + type: object + name: + type: string + server_name: + type: string + type: + type: string + enum: [mcp_tool_use] + cache_control: + $ref: '#/components/schemas/CacheControl' + + RequestMCPToolResultBlockContentObject: + type: object + required: + - text + - type + properties: + text: + type: string + minLength: 1 + type: + type: string + enum: [text] + cache_control: + $ref: '#/components/schemas/CacheControl' + citations: + $ref: '#/components/schemas/Citations' + + RequestMCPToolResultBlock: + type: object + required: + - tool_use_id + - type + properties: + tool_use_id: + type: string + type: + type: string + enum: [mcp_tool_result] + cache_control: + $ref: '#/components/schemas/CacheControl' + content: + oneOf: + - type: string + - type: array + items: + $ref: '#/components/schemas/RequestMCPToolResultBlockContentObject' + is_error: + type: boolean + + Text: + type: object + required: + - text + - type + properties: + text: + type: string + minLength: 1 + type: + type: string + enum: [text] + cache_control: + $ref: '#/components/schemas/CacheControl' + citations: + $ref: '#/components/schemas/Citations' + + ImageBase64: + type: object + required: + - data + - media_type + - type + properties: + data: + type: string + media_type: + type: string + enum: [image/jpeg, image/png, image/gif, image/webp] + type: + type: string + enum: [base64] + + URL: + type: object + required: + - type + - url + properties: + type: + type: string + enum: [url] + url: + type: string + + File: + type: object + required: + - file_id + - type + properties: + file_id: + type: string + type: + type: string + enum: [file] + + Image: + type: object + required: + - source + - type + properties: + source: + oneOf: + - $ref: '#/components/schemas/ImageBase64' + - $ref: '#/components/schemas/URL' + - $ref: '#/components/schemas/File' + type: + type: string + enum: [image] + cache_control: + $ref: '#/components/schemas/CacheControl' + + ToolUse: + type: object + required: + - id + - input + - name + - type + properties: + id: + type: string + input: + type: object + name: + type: string + minLength: 1 + maxLength: 200 + type: + type: string + enum: [tool_use] + cache_control: + $ref: '#/components/schemas/CacheControl' + + ToolResult: + type: object + required: + - tool_use_id + - type + properties: + tool_use_id: + type: string + type: + type: string + enum: [tool_result] + cache_control: + $ref: '#/components/schemas/CacheControl' + content: + oneOf: + - type: string + - type: object + oneOf: + - $ref: '#/components/schemas/Text' + - $ref: '#/components/schemas/Image' + is_error: + type: boolean + + Base64PDF: + type: object + required: + - data + - media_type + - type + properties: + data: + type: string + media_type: + type: string + enum: [application/pdf] + type: + type: string + enum: [base64] + + PlainTextPDF: + type: object + required: + - data + - media_type + - type + properties: + data: + type: string + media_type: + type: string + enum: [text/plain] + type: + type: string + enum: [text] + + ContentBlockPDF: + type: object + required: + - content + - type + properties: + content: + oneOf: + - type: string + - type: object + oneOf: + - $ref: '#/components/schemas/Text' + - $ref: '#/components/schemas/Image' + type: + type: string + enum: [content] + + Document: + type: object + required: + - source + - type + properties: + source: + type: object + oneOf: + - $ref: '#/components/schemas/Base64PDF' + - $ref: '#/components/schemas/PlainTextPDF' + - $ref: '#/components/schemas/ContentBlockPDF' + - $ref: '#/components/schemas/URL' + - $ref: '#/components/schemas/File' + type: + type: string + enum: [document] + cache_control: + $ref: '#/components/schemas/CacheControl' + citations: + type: object + properties: + enabled: + type: boolean + context: + type: string + nullable: true + minLength: 1 + title: + type: string + nullable: true + minLength: 1 + maxLength: 500 + + Thinking: + type: object + required: + - signature + - thinking + - type + properties: + signature: + type: string + thinking: + type: string + type: + type: string + enum: [thinking] + + RedactedThinking: + type: object + required: + - data + - type + properties: + data: + type: string + type: + type: string + enum: [redacted_thinking] + + RequestContainerUploadBlock: + type: object + required: + - file_id + - type + properties: + file_id: + type: string + type: + type: string + enum: [container_upload] + cache_control: + $ref: '#/components/schemas/CacheControl' + + Message: + type: object + required: + - role + - content + properties: + role: + type: string + enum: [user, assistant] + content: + oneOf: + - type: string + - type: array + items: + type: object + required: + - type + oneOf: + - $ref: '#/components/schemas/ServerToolUse' + - $ref: '#/components/schemas/WebSearchToolResult' + - $ref: '#/components/schemas/RequestCodeExecutionToolResultBlock' + - $ref: '#/components/schemas/RequestMCPToolUseBlock' + - $ref: '#/components/schemas/RequestMCPToolResultBlock' + - $ref: '#/components/schemas/Text' + - $ref: '#/components/schemas/Image' + - $ref: '#/components/schemas/ToolUse' + - $ref: '#/components/schemas/ToolResult' + - $ref: '#/components/schemas/Document' + - $ref: '#/components/schemas/Thinking' + - $ref: '#/components/schemas/RedactedThinking' + - $ref: '#/components/schemas/RequestContainerUploadBlock' + + ToolConfiguration: + type: object + nullable: true + properties: + allowed_tools: + type: array + nullable: true + items: + type: string + enabled: + type: boolean + nullable: true + + MCPServers: + type: object + required: + - name + - type + properties: + name: + type: string + type: + type: string + enum: [url] + url: + type: string + authorization_token: + type: string + nullable: true + tool_configuration: + $ref: '#/components/schemas/ToolConfiguration' + + ThinkingEnabled: + type: object + required: + - type + properties: + type: + type: string + enum: [enabled] + budget_tokens: + type: integer + minimum: 1024 + description: Tokens allocated for Claude's internal reasoning. Must be at least 1024 and cannot exceed max_tokens value. + + ThinkingDisabled: + type: object + required: + - type + properties: + type: + type: string + enum: [disabled] + + ToolChoiceAuto: + type: object + description: Automatically choose whether and which tool to use + required: + - type + properties: + type: + type: string + enum: [auto] + disable_parallel_tool_use: + type: boolean + default: false + description: Whether to disable parallel tool use + + ToolChoiceAny: + type: object + description: Automatically choose whether and which tool to use + required: + - type + properties: + type: + type: string + enum: [any] + disable_parallel_tool_use: + type: boolean + default: false + description: Whether to disable parallel tool use + + ToolChoiceTool: + type: object + description: Automatically choose whether and which tool to use + required: + - name + - type + properties: + name: + type: string + description: The name of the tool to use + type: + type: string + enum: [tool] + disable_parallel_tool_use: + type: boolean + default: false + description: Whether to disable parallel tool use + + ToolChoiceNone: + type: object + description: Do not use any tools + required: + - type + properties: + type: + type: string + enum: [none] + + ToolInputSchema: + type: object + required: + - type + properties: + type: + type: string + enum: [object] + properties: + type: object + nullable: true + + Tool: + type: object + required: + - input_schema + - name + properties: + input_schema: + $ref: '#/components/schemas/ToolInputSchema' + name: + type: string + minLength: 1 + maxLength: 64 + cache_control: + $ref: '#/components/schemas/CacheControl' + description: + type: string + type: + type: string + nullable: true + enum: [custom] + + ComputerUseTool20241022: + type: object + required: + - display_height_px + - display_width_px + - name + - type + properties: + display_height_px: + type: integer + minimum: 2 + display_width_px: + type: integer + minimum: 2 + name: + type: string + enum: [computer] + type: + type: string + enum: [computer_20241022] + cache_control: + $ref: '#/components/schemas/CacheControl' + display_number: + type: integer + nullable: true + minimum: 1 + + BashTool20241022: + type: object + required: + - name + - type + properties: + name: + type: string + enum: [bash] + type: + type: string + enum: [bash_20241022] + cache_control: + $ref: '#/components/schemas/CacheControl' + + TextEditorTool20241022: + type: object + required: + - name + - type + properties: + name: + type: string + enum: [str_replace_editor] + type: + type: string + enum: [text_editor_20241022] + cache_control: + $ref: '#/components/schemas/CacheControl' + + ComputerUseTool20250124: + type: object + required: + - display_height_px + - display_width_px + - name + - type + properties: + display_height_px: + type: integer + minimum: 2 + display_width_px: + type: integer + minimum: 2 + name: + type: string + enum: [computer] + type: + type: string + enum: [computer_20250124] + cache_control: + $ref: '#/components/schemas/CacheControl' + display_number: + type: integer + nullable: true + minimum: 1 + + BashTool20250124: + type: object + required: + - name + - type + properties: + name: + type: string + enum: [bash] + type: + type: string + enum: [bash_20250124] + cache_control: + $ref: '#/components/schemas/CacheControl' + + TextEditorTool20250124: + type: object + required: + - name + - type + properties: + name: + type: string + enum: [str_replace_editor] + type: + type: string + enum: [text_editor_20250124] + cache_control: + $ref: '#/components/schemas/CacheControl' + + TextEditor20250429: + type: object + required: + - name + - type + properties: + name: + type: string + enum: [str_replace_based_edit_tool] + type: + type: string + enum: [text_editor_20250429] + cache_control: + $ref: '#/components/schemas/CacheControl' + + UserLocation: + type: object + nullable: true + properties: + type: + type: string + enum: [approximate] + city: + type: string + nullable: true + minLength: 1 + maxLength: 256 + country: + type: string + nullable: true + minLength: 2 + maxLength: 2 + description: ISO Code of the country. + region: + type: string + nullable: true + minLength: 1 + maxLength: 256 + timezone: + type: string + nullable: true + minLength: 1 + maxLength: 256 + description: IANA timezone of the user. + + WebSearchTool20250305: + type: object + required: + - name + - type + properties: + name: + type: string + enum: [web_search] + type: + type: string + enum: [web_search_20250305] + allowed_domains: + type: array + nullable: true + items: + type: string + description: If provided, only these domains will be included in results. Cannot be used alongside blocked_domains. + blocked_domains: + type: array + nullable: true + items: + type: string + description: If provided, these domains will never appear in results. Cannot be used alongside allowed_domains. + cache_control: + $ref: '#/components/schemas/CacheControl' + max_uses: + type: integer + nullable: true + minimum: 1 + user_location: + $ref: '#/components/schemas/UserLocation' + + CodeExecutionTool20250522: + type: object + required: + - name + - type + properties: + name: + type: string + enum: [code_execution] + type: + type: string + enum: [code_execution_20250522] + cache_control: + $ref: '#/components/schemas/CacheControl' + + Container: + type: object + nullable: true + required: + - expires_at + - id + properties: + expires_at: + type: string + format: date-time + id: + type: string + + TextResponse: + type: object + required: + - citations + - text + - type + properties: + citations: + $ref: '#/components/schemas/Citations' + text: + type: string + maxLength: 5000000 + type: + type: string + enum: [text] + + ToolUseResponse: + type: object + required: + - id + - input + - name + - type + properties: + id: + type: string + input: + type: object + name: + type: string + minLength: 1 + type: + type: string + enum: [tool_use] + + ResponseServerToolUseBlock: + type: object + required: + - id + - input + - name + - type + properties: + id: + type: string + input: + type: object + name: + type: string + minLength: 1 + type: + type: string + enum: [server_tool_use] + + ResponseWebSearchToolResultBlock: + type: object + required: + - content + - tool_use_id + - type + properties: + content: + oneOf: + - $ref: '#/components/schemas/WebSearchToolResultObject' + - $ref: '#/components/schemas/WebSearchToolResultObjectArray' + tool_use_id: + type: string + type: + type: string + enum: [web_search_tool_result] + + ResponseCodeExecutionToolResultBlock: + type: object + required: + - content + - tool_use_id + - type + properties: + content: + type: object + oneOf: + - $ref: '#/components/schemas/RequestCodeExecutionToolResultBlockErrorContent' + - $ref: '#/components/schemas/RequestCodeExecutionToolResultBlockContent' + tool_use_id: + type: string + type: + type: string + enum: [code_execution_tool_result] + + ResponseMCPToolUseBlock: + type: object + required: + - id + - input + - name + - server_name + - type + properties: + id: + type: string + input: + type: object + name: + type: string + server_name: + type: string + type: + type: string + enum: [mcp_tool_use] + + ResponseMCPToolResultBlock: + type: object + required: + - content + - is_error + - tool_use_id + - type + properties: + content: + oneOf: + - type: string + - $ref: '#/components/schemas/TextResponse' + is_error: + type: boolean + default: false + tool_use_id: + type: string + type: + type: string + enum: [mcp_tool_result] + + ResponseContainerUploadBlock: + type: object + required: + - file_id + - type + properties: + file_id: + type: string + type: + type: string + enum: [container_upload] + + CacheCreationUsage: + type: object + nullable: true + required: + - ephemeral_1h_input_tokens + - ephemeral_5m_input_tokens + properties: + ephemeral_1h_input_tokens: + type: integer + default: 0 + minimum: 1 + ephemeral_5m_input_tokens: + type: integer + default: 0 + minimum: 1 + + ServerToolUseUsage: + type: object + nullable: true + required: + - web_search_requests + properties: + web_search_requests: + type: integer + default: 0 + minimum: 1 + description: The number of web search tool requests. + + Usage: + type: object + required: + - cache_creation + - cache_creation_input_tokens + - cache_read_input_tokens + - input_tokens + - output_tokens + - server_tool_use + description: | + Billing and rate-limit usage. + properties: + cache_creation: + $ref: '#/components/schemas/CacheCreationUsage' + cache_creation_input_tokens: + type: integer + nullable: true + minimum: 1 + cache_read_input_tokens: + type: integer + nullable: true + minimum: 1 + input_tokens: + type: integer + minimum: 1 + output_tokens: + type: integer + minimum: 1 + server_tool_use: + $ref: '#/components/schemas/ServerToolUseUsage' + service_tier: + type: string + nullable: true + enum: [standard, priority, batch] + + MessageRequest: + type: array + description: A list of messages comprising the conversation so far. + items: + $ref: '#/components/schemas/Message' + maxItems: 100000 + + ModelRequest: + type: string + minLength: 1 + maxLength: 256 + description: The model that will complete your prompt. Must be between 1 and 256 characters. + + SystemRequest: + oneOf: + - type: string + - type: object # Move to Content + required: + - text + - type + properties: + text: + type: string + minLength: 1 + type: + type: string + enum: [text] + cache_control: + $ref: '#/components/schemas/CacheControl' + citations: + $ref: '#/components/schemas/Citations' + description: A system prompt providing context and instructions to Claude, such as specifying a particular goal or role. Can be provided either as a simple string or as a structured object with cache control options. + + ThinkingRequest: + oneOf: + - $ref: '#/components/schemas/ThinkingEnabled' + - $ref: '#/components/schemas/ThinkingDisabled' + + ToolChoiceRequest: + oneOf: + - $ref: '#/components/schemas/ToolChoiceAuto' + - $ref: '#/components/schemas/ToolChoiceAny' + - $ref: '#/components/schemas/ToolChoiceTool' + - $ref: '#/components/schemas/ToolChoiceNone' + + ToolsRequest: + type: array + items: + oneOf: + - $ref: '#/components/schemas/Tool' + - $ref: '#/components/schemas/ComputerUseTool20241022' + - $ref: '#/components/schemas/BashTool20241022' + - $ref: '#/components/schemas/TextEditorTool20241022' + - $ref: '#/components/schemas/ComputerUseTool20250124' + - $ref: '#/components/schemas/BashTool20250124' + - $ref: '#/components/schemas/TextEditorTool20250124' + - $ref: '#/components/schemas/TextEditor20250429' + - $ref: '#/components/schemas/WebSearchTool20250305' + - $ref: '#/components/schemas/CodeExecutionTool20250522' + description: List of tools that the model may use during the conversation + + MessageRequestBody: + type: object + required: + - max_tokens + - messages + - model + properties: + max_tokens: + type: integer + minimum: 1 + description: The maximum number of tokens to generate before stopping. Models may stop before reaching this maximum. + messages: + $ref: '#/components/schemas/MessageRequest' + model: + $ref: '#/components/schemas/ModelRequest' + container: + type: string + nullable: true + description: Container identifier for reuse across requests. + mcp_servers: + $ref: '#/components/schemas/MCPServers' + metadata: + type: object + properties: + user_id: + type: string + nullable: true + description: A user identifier for tracing the request. + maxLength: 256 + description: An object describing metadata about the request. Can include user_id for tracking purposes. + service_tier: + type: string + enum: [auto, standard_only] + description: 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 for details. + stop_sequences: + type: array + items: + type: string + description: Custom text sequences that will cause the model to stop generating. The model will stop when it encounters any of these sequences. + stream: + type: boolean + description: Whether to incrementally stream the response using server-sent events. + system: + $ref: '#/components/schemas/SystemRequest' + temperature: + type: number + minimum: 0 + maximum: 1 + description: Amount of randomness injected into the response. Defaults to 1.0. Use lower values for analytical tasks and higher values for creative tasks. + thinking: + $ref: '#/components/schemas/ThinkingRequest' + tool_choice: + $ref: '#/components/schemas/ToolChoiceRequest' + tools: + $ref: '#/components/schemas/ToolsRequest' + top_k: + type: integer + minimum: 0 + description: Only sample from the top K options for each subsequent token + top_p: + type: number + minimum: 0 + maximum: 1 + description: Only sample from tokens with cumulative probability less than this value + + MessageResponse: + type: object + required: + - container + - content + - id + - model + - role + - stop_reason + - stop_sequence + - type + - usage + properties: + container: + $ref: '#/components/schemas/Container' + content: + type: array + description: Content generated by the model. This is an array of content blocks, each of which has a type that determines its shape. + items: + oneOf: + - $ref: '#/components/schemas/TextResponse' + - $ref: '#/components/schemas/ToolUseResponse' + - $ref: '#/components/schemas/ResponseServerToolUseBlock' + - $ref: '#/components/schemas/ResponseWebSearchToolResultBlock' + - $ref: '#/components/schemas/ResponseCodeExecutionToolResultBlock' + - $ref: '#/components/schemas/ResponseMCPToolUseBlock' + - $ref: '#/components/schemas/ResponseMCPToolResultBlock' + - $ref: '#/components/schemas/ResponseContainerUploadBlock' + - $ref: '#/components/schemas/Thinking' + - $ref: '#/components/schemas/RedactedThinking' + id: + type: string + description: Unique object identifier. The format and length of IDs may change over time. + model: + type: string + minLength: 1 + maxLength: 256 + description: The model that handled the request. + role: + type: string + enum: [assistant] + default: assistant + description: Conversational role of the generated message. This will always be "assistant". + stop_reason: + type: string + nullable: true + enum: [end_turn, max_tokens, stop_sequence, tool_use, pause_turn, refusal] + description: | + The reason that we stopped. This may be one of 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 + + 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: + type: string + nullable: true + description: 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: + type: string + enum: [message] + default: message + description: Object type. For Messages, this is always "message". + usage: + $ref: '#/components/schemas/Usage' + + ModelResponseData: + type: object + required: + - created_at + - display_name + - id + - type + properties: + created_at: + type: string + format: date-time + description: 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: + type: string + description: A human-readable name for the model. + id: + type: string + description: Unique model identifier. + type: + type: string + enum: [model] + default: model + description: Object type. For Models, this is always "model". + + FirstId: + type: string + nullable: true + description: First ID in the data list. Can be used as the before_id for the previous page. + + HasMore: + type: boolean + description: Indicates if there are more results in the requested page direction. + + LastId: + type: string + nullable: true + description: Last ID in the data list. Can be used as the after_id for the next page. + + RequestsCount: + type: object + required: + - canceled + - errored + - expired + - processing + - succeeded + properties: + canceled: + type: integer + default: 0 + errored: + type: integer + default: 0 + expired: + type: integer + default: 0 + processing: + type: integer + default: 0 + succeeded: + type: integer + default: 0 + + MessageBatchResponse: + type: object + required: + - archived_at + - cancel_initiated_at + - created_at + - ended_at + - expires_at + - id + - processing_status + - request_counts + - results_url + - type + properties: + archived_at: + type: string + format: date-time + nullable: true + cancel_initiated_at: + type: string + format: date-time + nullable: true + created_at: + type: string + format: date-time + ended_at: + type: string + format: date-time + nullable: true + expires_at: + type: string + format: date-time + id: + type: string + processing_status: + type: string + enum: [in_progress, canceling, ended] + request_counts: + $ref: '#/components/schemas/RequestsCount' + results_url: + type: string + nullable: true + type: + type: string + enum: [message_batch] + default: message_batch + + MessageBatchSucceededResult: + type: object + required: + - message + - type + properties: + message: + $ref: '#/components/schemas/MessageResponse' + type: + type: string + enum: [succeeded] + + MessageBatchError: + type: object + required: + - type + - error + properties: + type: + type: string + enum: [error] + error: + type: object + required: + - message + - type + properties: + message: + type: string # TODO: IN THE FUTURE THIS WILL BE A "oneOf" of defined error types to specify the error messages + type: + type: string + enum: + - invalid_request_error + - authentication_error + - billing_error + - permission_error + - not_found_error + - rate_limit_error + - timeout_error + - internal_server_error + - overloaded_error + + MessageBatchErroredResult: + type: object + required: + - error + - type + properties: + type: + type: string + enum: [errored] + error: + $ref: '#/components/schemas/MessageBatchError' + + MessageBatchCanceledResult: + type: object + required: + - type + properties: + type: + type: string + enum: [canceled] + + MessageBatchExpiredResult: + type: object + required: + - type + properties: + type: + type: string + enum: [expired] + + FileData: + type: object + required: + - created_at + - filename + - id + - mime_type + - size_bytes + - type + properties: + created_at: + type: string + format: date-time + description: RFC 3339 datetime string representing when the file was created + filename: + type: string + minLength: 1 + maxLength: 500 + description: Original filename of the uploaded file + id: + type: string + description: | + Unique object identifier. + The format and length of IDs may change over time. + mime_type: + type: string + minLength: 1 + maxLength: 255 + description: MIME type of the file + size_bytes: + type: integer + minimum: 0 + description: Size of the file in bytes + type: + type: string + enum: [ "file" ] + downloadable: + type: boolean + default: false + description: Whether the file can be downloaded + + AdminMemberManagementUserData: + type: object + required: + - added_at + - email + - id + - name + - role + - type + properties: + added_at: + type: string + format: date-time + description: RFC 3339 datetime string indicating when the User joined the Organization. + email: + type: string + description: Email of the User. + id: + type: string + description: ID of the User. + name: + type: string + description: Name of the User. + role: + type: string + enum: [user, developer, billing, admin] + description: Organization role of the User. + type: + type: string + enum: [user] + default: user + description: Object type. For Users, this is always "user". + + AdminInviteData: + type: object + required: + - email + - expires_at + - id + - invited_at + - role + - status + - type + properties: + email: + type: string + description: Email of the User. + expires_at: + type: string + description: RFC 3339 datetime string indicating when the invite expires. + id: + type: string + description: ID of the User. + invited_at: + type: string + description: RFC 3339 datetime string indicating when the invite was created. + role: + type: string + enum: [user, developer, billing, admin] + description: Organization role of the User. + status: + type: string + enum: [accepted, expired, deleted, pending] + description: Status of the invite. + type: + type: string + enum: [invite] + default: invite + description: Object type. For Invites, this is always "invite". + + AdminWorkspaceData: + type: object + required: + - archived_at + - created_at + - display_color + - id + - name + - type + properties: + archived_at: + type: string + nullable: true + description: RFC 3339 datetime string indicating when the Workspace was archived. + created_at: + type: string + display_color: + type: string + description: Hexadecimal color code for the Workspace. + id: + type: string + name: + type: string + type: + type: string + enum: [workspace] + default: workspace + + AdminWorkspaceMemberData: + type: object + required: + - type + - user_id + - workspace_id + - workspace_role + properties: + type: + type: string + enum: [workspace_member] + default: workspace_member + description: Object type. For Workspace Members, this is always "workspace_member". + user_id: + type: string + description: ID of the User. + workspace_id: + type: string + description: ID of the Workspace. + workspace_role: + type: string + enum: [workspace_user, workspace_developer, workspace_admin, workspace_billing] + description: Role of the Workspace Member. + + AdminAPIKeyData: + type: object + required: + - created_at + - created_by + - id + - name + - partial_key_hint + - status + - type + - workspace_id + properties: + created_at: + type: string + description: RFC 3339 datetime string indicating when the API Key was created. + created_by: + type: object + required: + - id + - type + properties: + id: + type: string + description: ID of the actor that created the object. + type: + type: string + description: Type of the actor that created the object. + description: The ID and type of the actor that created the API Key. + id: + type: string + description: ID of the API key. + name: + type: string + description: Name of the API key. + partial_key_hint: + type: string + nullable: true + description: Partially redacted hint for the API key. + status: + type: string + enum: [active, inactive, archived] + description: Status of the API key. + type: + type: string + enum: [api_key] + default: api_key + description: Object type. For API Keys, this is always "api_key". + workspace_id: + type: string + nullable: true + description: ID of the Workspace associated with the API key, or null if the API key belongs to the default Workspace. + + Error: + type: object + required: + - type + - error + properties: + type: + type: string + enum: [error] + error: + type: object + required: + - type + - message + properties: + type: + type: string + enum: + - invalid_request_error + - authentication_error + - permission_error + - not_found_error + - request_too_large + - rate_limit_error + - api_error + - overloaded_error + message: + type: string + + responses: + Error400: + description: Invalid Request Error - There was an issue with the format or content of your request + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + Error401: + description: Authentication Error - There's an issue with your API key + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + Error403: + description: Permission Error - Your API key does not have permission to use the specified resource + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + Error404: + description: Not Found Error - The requested resource was not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + Error413: + description: Request Too Large - Request exceeds the maximum allowed number of bytes + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + Error429: + description: Rate Limit Error - Your account has hit a rate limit + headers: + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + Error500: + description: API Error - An unexpected error has occurred internal to Anthropic's systems + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + + Error529: + description: Overloaded Error - Anthropic's API is temporarily overloaded + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + +paths: + /v1/messages: + post: + tags: + - Messages + - text-generation + summary: Create a chat completion + description: Creates a chat completion with the specified model and parameters. The response is generated based on the provided messages and system prompt. + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/AnthropicBetaHeader' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/MessageRequestBody' + responses: + '200': + description: Successful response + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + anthropic-ratelimit-requests-limit: + $ref: '#/components/headers/AnthropicRateLimitRequestsLimit' + anthropic-ratelimit-requests-remaining: + $ref: '#/components/headers/AnthropicRateLimitRequestsRemaining' + anthropic-ratelimit-requests-reset: + $ref: '#/components/headers/AnthropicRateLimitRequestsReset' + anthropic-ratelimit-tokens-limit: + $ref: '#/components/headers/AnthropicRateLimitTokensLimit' + anthropic-ratelimit-tokens-remaining: + $ref: '#/components/headers/AnthropicRateLimitTokensRemaining' + anthropic-ratelimit-tokens-reset: + $ref: '#/components/headers/AnthropicRateLimitTokensReset' + anthropic-ratelimit-input-tokens-limit: + $ref: '#/components/headers/AnthropicRateLimitInputTokensLimit' + anthropic-ratelimit-input-tokens-remaining: + $ref: '#/components/headers/AnthropicRateLimitInputTokensRemaining' + anthropic-ratelimit-input-tokens-reset: + $ref: '#/components/headers/AnthropicRateLimitInputTokensReset' + anthropic-ratelimit-output-tokens-limit: + $ref: '#/components/headers/AnthropicRateLimitOutputTokensLimit' + anthropic-ratelimit-output-tokens-remaining: + $ref: '#/components/headers/AnthropicRateLimitOutputTokensRemaining' + anthropic-ratelimit-output-tokens-reset: + $ref: '#/components/headers/AnthropicRateLimitOutputTokensReset' + anthropic-priority-input-tokens-limit: + $ref: '#/components/headers/AnthropicPriorityInputTokensLimit' + anthropic-priority-input-tokens-remaining: + $ref: '#/components/headers/AnthropicPriorityInputTokensRemaining' + anthropic-priority-input-tokens-reset: + $ref: '#/components/headers/AnthropicPriorityInputTokensReset' + anthropic-priority-output-tokens-limit: + $ref: '#/components/headers/AnthropicPriorityOutputTokensLimit' + anthropic-priority-output-tokens-remaining: + $ref: '#/components/headers/AnthropicPriorityOutputTokensRemaining' + anthropic-priority-output-tokens-reset: + $ref: '#/components/headers/AnthropicPriorityOutputTokensReset' + content: + application/json: + schema: + $ref: '#/components/schemas/MessageResponse' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + /v1/messages/count_tokens: + post: + tags: + - Messages + summary: Count tokens for a chat completion + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/AnthropicBetaHeader' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - messages + - model + properties: + messages: + $ref: '#/components/schemas/MessageRequest' + model: + $ref: '#/components/schemas/ModelRequest' + mcp_servers: + $ref: '#/components/schemas/MCPServers' + system: + $ref: '#/components/schemas/SystemRequest' + thinking: + $ref: '#/components/schemas/ThinkingRequest' + tool_choice: + $ref: '#/components/schemas/ToolChoiceRequest' + tools: + $ref: '#/components/schemas/ToolsRequest' + responses: + '200': + description: Successful response + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + type: object + required: + - input_tokens + properties: + input_tokens: + type: integer + description: The total number of tokens across the provided list of messages, system prompt, and tools. + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + /v1/models: + get: + tags: + - Models + summary: List Models + description: 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. + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/BeforeId' + - $ref: '#/components/parameters/AfterId' + - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + responses: + '200': + description: Successfully retrieved models + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + type: object + required: + - data + - first_id + - has_more + - last_id + properties: + data: + type: array + items: + $ref: '#/components/schemas/ModelResponseData' + first_id: + $ref: '#/components/schemas/FirstId' + has_more: + $ref: '#/components/schemas/HasMore' + last_id: + $ref: '#/components/schemas/LastId' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + /v1/models/{model_id}: + get: + tags: + - Models + summary: Get a Model + description: 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. + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + - name: model_id + in: path + required: true + schema: + type: string + description: Model identifier or alias. + responses: + '200': + description: Successfully retrieved model + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + $ref: '#/components/schemas/ModelResponseData' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + /v1/messages/batches: + post: + tags: + - Message Batches + - text-generation + summary: Create multiple chat completions in a batch + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/AnthropicBetaHeader' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - requests + properties: + requests: + type: array + items: + type: object + required: + - custom_id + - params + properties: + custom_id: + type: string + minLength: 1 + maxLength: 64 + params: + $ref: '#/components/schemas/MessageRequestBody' + responses: + '200': + description: Successfully created message batch + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + anthropic-ratelimit-requests-limit: + $ref: '#/components/headers/AnthropicRateLimitRequestsLimit' + anthropic-ratelimit-requests-remaining: + $ref: '#/components/headers/AnthropicRateLimitRequestsRemaining' + anthropic-ratelimit-requests-reset: + $ref: '#/components/headers/AnthropicRateLimitRequestsReset' + anthropic-ratelimit-tokens-limit: + $ref: '#/components/headers/AnthropicRateLimitTokensLimit' + anthropic-ratelimit-tokens-remaining: + $ref: '#/components/headers/AnthropicRateLimitTokensRemaining' + anthropic-ratelimit-tokens-reset: + $ref: '#/components/headers/AnthropicRateLimitTokensReset' + anthropic-ratelimit-input-tokens-limit: + $ref: '#/components/headers/AnthropicRateLimitInputTokensLimit' + anthropic-ratelimit-input-tokens-remaining: + $ref: '#/components/headers/AnthropicRateLimitInputTokensRemaining' + anthropic-ratelimit-input-tokens-reset: + $ref: '#/components/headers/AnthropicRateLimitInputTokensReset' + anthropic-ratelimit-output-tokens-limit: + $ref: '#/components/headers/AnthropicRateLimitOutputTokensLimit' + anthropic-ratelimit-output-tokens-remaining: + $ref: '#/components/headers/AnthropicRateLimitOutputTokensRemaining' + anthropic-ratelimit-output-tokens-reset: + $ref: '#/components/headers/AnthropicRateLimitOutputTokensReset' + anthropic-priority-input-tokens-limit: + $ref: '#/components/headers/AnthropicPriorityInputTokensLimit' + anthropic-priority-input-tokens-remaining: + $ref: '#/components/headers/AnthropicPriorityInputTokensRemaining' + anthropic-priority-input-tokens-reset: + $ref: '#/components/headers/AnthropicPriorityInputTokensReset' + anthropic-priority-output-tokens-limit: + $ref: '#/components/headers/AnthropicPriorityOutputTokensLimit' + anthropic-priority-output-tokens-remaining: + $ref: '#/components/headers/AnthropicPriorityOutputTokensRemaining' + anthropic-priority-output-tokens-reset: + $ref: '#/components/headers/AnthropicPriorityOutputTokensReset' + content: + application/json: + schema: + $ref: '#/components/schemas/MessageBatchResponse' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + get: + tags: + - Message Batches + summary: List Message Batches + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/BeforeId' + - $ref: '#/components/parameters/AfterId' + - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + responses: + '200': + description: Successfully retrieved message batches + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + type: object + required: + - data + - first_id + - has_more + - last_id + properties: + data: + type: array + items: + $ref: '#/components/schemas/MessageBatchResponse' + has_more: + type: boolean + description: Indicates if there are more results in the requested page direction. + first_id: + type: string + nullable: true + description: First ID in the data list. Can be used as the before_id for the previous page. + last_id: + type: string + nullable: true + description: Last ID in the data list. Can be used as the after_id for the next page. + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + /v1/messages/batches/{message_batch_id}: + get: + tags: + - Message Batches + summary: Retrieve a Message Batch + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/BatchId' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + responses: + '200': + description: Successfully retrieved message batch + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + $ref: '#/components/schemas/MessageBatchResponse' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + delete: + tags: + - Message Batches + summary: Delete a Message Batch + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/BatchId' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + responses: + '200': + description: Successfully deleted message batch + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + type: object + required: + - id + - type + properties: + id: + type: string + type: + type: string + enum: [message_batch_deleted] + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + /v1/messages/batches/{message_batch_id}/results: + get: + tags: + - Message Batches + summary: Retrieve Message Batch Results. The path for retrieving Message Batch results should be pulled from the batch's results_url. This path should not be assumed and may change. + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/BatchId' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + responses: + '200': + description: Successfully retrieved batch results + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/x-jsonl: + schema: + required: + - custom_id + - result + properties: + custom_id: + type: string + description: 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: + type: object + oneOf: + - $ref: '#/components/schemas/MessageBatchSucceededResult' + - $ref: '#/components/schemas/MessageBatchErroredResult' + - $ref: '#/components/schemas/MessageBatchCanceledResult' + - $ref: '#/components/schemas/MessageBatchExpiredResult' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + /v1/messages/batches/{message_batch_id}/cancel: + post: + tags: + - Message Batches + summary: Cancel a Message Batch + description: Cancels an in-progress message batch. Non-interruptible requests may complete before cancellation is finalized. + security: + - ApiKeyAuth: [] + parameters: + - $ref: '#/components/parameters/AnthropicVersionHeader' + - $ref: '#/components/parameters/BatchId' + - $ref: '#/components/parameters/AnthropicDangerousDirectBrowserAccessHeader' + responses: + '200': + description: Successfully initiated batch cancellation + headers: + request-id: + $ref: '#/components/headers/RequestId' + anthropic-organization-id: + $ref: '#/components/headers/AnthropicOrganizationId' + retry-after: + $ref: '#/components/headers/RetryAfter' + content: + application/json: + schema: + $ref: '#/components/schemas/MessageBatchResponse' + '400': + $ref: '#/components/responses/Error400' + '401': + $ref: '#/components/responses/Error401' + '403': + $ref: '#/components/responses/Error403' + '404': + $ref: '#/components/responses/Error404' + '413': + $ref: '#/components/responses/Error413' + '429': + $ref: '#/components/responses/Error429' + '500': + $ref: '#/components/responses/Error500' + '529': + $ref: '#/components/responses/Error529' + + \ No newline at end of file diff --git a/tests/ai-workspace-cli-e2e/resources/llm-proxy/edit/metadata.yaml b/tests/ai-workspace-cli-e2e/resources/llm-proxy/edit/metadata.yaml new file mode 100644 index 000000000..7f1173004 --- /dev/null +++ b/tests/ai-workspace-cli-e2e/resources/llm-proxy/edit/metadata.yaml @@ -0,0 +1,16 @@ +apiVersion: ai-workspace.api-platform.wso2.com/v1alpha +kind: LlmProxy +metadata: + name: claude-proxy +spec: + displayName: claude Proxy + version: v1.0 + # associatedGateways must live under spec for the CLI to fold it into the + # payload. These gateway handles are registered by the suite before apply. + associatedGateways: + - id: prod-eu-01 + configurations: + host: prod-eu-01.api.wso2.com + - id: prod-eu-02 + configurations: + host: prod-eu-02.api.wso2.com diff --git a/tests/ai-workspace-cli-e2e/resources/llm-proxy/edit/runtime.yaml b/tests/ai-workspace-cli-e2e/resources/llm-proxy/edit/runtime.yaml new file mode 100644 index 000000000..3d7e2e1a7 --- /dev/null +++ b/tests/ai-workspace-cli-e2e/resources/llm-proxy/edit/runtime.yaml @@ -0,0 +1,86 @@ +apiVersion: gateway.api-platform.wso2.com/v1 +kind: LlmProxy +metadata: + name: claude-proxy +spec: + displayName: wso2 claude proxy + version: v1.0 + context: /default/claude-proxy + provider: + id: claude-provider + auth: + header: X-API-Key + type: api-key + globalPolicies: + - name: api-key-auth + params: + in: header + key: X-API-Key + version: "" + - name: aws-bedrock-guardrail + params: + request: + enabled: true + jsonPath: $.messages[-1].content + passthroughOnError: false + redactPII: false + showAssessment: false + response: + enabled: false + jsonPath: $.choices[0].message.content + passthroughOnError: false + showAssessment: false + version: v1 + operationPolicies: + - name: basic-auth + paths: + - methods: + - POST + params: + allowUnauthenticated: false + password: admin + realm: Restricted + username: admin + path: /v1/messages + version: v1 + - name: cors + paths: + - methods: + - POST + params: + allowCredentials: false + allowedHeaders: [] + allowedMethods: + - GET + - POST + - PUT + - DELETE + - OPTIONS + allowedOrigins: + - '*' + exposedHeaders: [] + forwardPreflight: false + maxAge: 3600 + path: /v1/messages + version: v1 + - name: content-length-guardrail + paths: + - methods: + - POST + params: + request: + enabled: true + invert: false + jsonPath: $.messages[-1].content + max: 0 + min: 0 + showAssessment: false + response: + enabled: false + invert: false + jsonPath: $.choices[0].message.content + max: 0 + min: 0 + showAssessment: false + path: /v1/messages + version: v1 diff --git a/tests/ai-workspace-cli-e2e/resources/mcp/create/metadata.yaml b/tests/ai-workspace-cli-e2e/resources/mcp/create/metadata.yaml new file mode 100644 index 000000000..b364ed83e --- /dev/null +++ b/tests/ai-workspace-cli-e2e/resources/mcp/create/metadata.yaml @@ -0,0 +1,7 @@ +apiVersion: ai-workspace.api-platform.wso2.com/v1alpha +kind: McpMetadata +metadata: + name: mcp-proxy +spec: + displayName: mcp-proxy + version: v1.0 diff --git a/tests/ai-workspace-cli-e2e/resources/mcp/create/runtime.yaml b/tests/ai-workspace-cli-e2e/resources/mcp/create/runtime.yaml new file mode 100644 index 000000000..d259d6c65 --- /dev/null +++ b/tests/ai-workspace-cli-e2e/resources/mcp/create/runtime.yaml @@ -0,0 +1,16 @@ +apiVersion: gateway.api-platform.wso2.com/v1 +kind: Mcp +metadata: + name: mcp-proxy + labels: + projectId: 019f2324-be88-7ecf-b15d-214ae1e3283d +spec: + displayName: mcp-proxy + version: v1.0 + context: /default/mcp-proxy + vhost: null + upstream: + url: https://db720294-98fd-40f4-85a1-cc6a3b65bc9a-prod.e1-us-east-azure.choreoapis.dev/godzilla/mcp-everything-server/v1.0 + auth: null + specVersion: "2025-06-18" + policies: diff --git a/tests/ai-workspace-cli-e2e/resources/mcp/definition.yaml b/tests/ai-workspace-cli-e2e/resources/mcp/definition.yaml new file mode 100644 index 000000000..97b056003 --- /dev/null +++ b/tests/ai-workspace-cli-e2e/resources/mcp/definition.yaml @@ -0,0 +1,127 @@ +prompts: + - description: A prompt without arguments + name: simple_prompt + - arguments: + - description: Temperature setting + name: temperature + required: true + - description: Output style + name: style + required: false + description: A prompt with arguments + name: complex_prompt + - arguments: + - description: Resource ID to include (1-100) + name: resourceId + required: true + description: A prompt that includes an embedded resource reference + name: resource_prompt +resources: + - mimeType: text/plain + name: Resource 1 + text: "Resource 1: This is a plaintext resource" + uri: test://static/resource/1 + - blob: UmVzb3VyY2UgMjogVGhpcyBpcyBhIGJhc2U2NCBibG9i + mimeType: application/octet-stream + name: Resource 2 + uri: test://static/resource/2 + - mimeType: text/plain + name: Resource 3 + text: "Resource 3: This is a plaintext resource" + uri: test://static/resource/3 + - blob: UmVzb3VyY2UgNDogVGhpcyBpcyBhIGJhc2U2NCBibG9i + mimeType: application/octet-stream + name: Resource 4 + uri: test://static/resource/4 + - mimeType: text/plain + name: Resource 5 + text: "Resource 5: This is a plaintext resource" + uri: test://static/resource/5 + - blob: UmVzb3VyY2UgNjogVGhpcyBpcyBhIGJhc2U2NCBibG9i + mimeType: application/octet-stream + name: Resource 6 + uri: test://static/resource/6 + - mimeType: text/plain + name: Resource 7 + text: "Resource 7: This is a plaintext resource" + uri: test://static/resource/7 + - blob: UmVzb3VyY2UgODogVGhpcyBpcyBhIGJhc2U2NCBibG9i + mimeType: application/octet-stream + name: Resource 8 + uri: test://static/resource/8 + - mimeType: text/plain + name: Resource 9 + text: "Resource 9: This is a plaintext resource" + uri: test://static/resource/9 + - blob: UmVzb3VyY2UgMTA6IFRoaXMgaXMgYSBiYXNlNjQgYmxvYg== + mimeType: application/octet-stream + name: Resource 10 + uri: test://static/resource/10 +serverInfo: + name: bijira-mcp-everything + version: 1.0.0 +tools: + - description: Echoes back the input + inputSchema: + $schema: http://json-schema.org/draft-07/schema# + additionalProperties: false + properties: + message: + description: Message to echo + type: string + required: + - message + type: object + name: echo + - description: Adds two numbers + inputSchema: + $schema: http://json-schema.org/draft-07/schema# + additionalProperties: false + properties: + a: + description: First number + type: number + b: + description: Second number + type: number + required: + - a + - b + type: object + name: add + - description: View the pizza menu. This tool provides a list of available pizzas. + inputSchema: + $schema: http://json-schema.org/draft-07/schema# + additionalProperties: false + properties: {} + type: object + name: viewPizzaMenu + - description: Order a pizza from the menu. This tool allows you to place an order for a pizza. + inputSchema: + $schema: http://json-schema.org/draft-07/schema# + additionalProperties: false + properties: + creditCardNumber: + description: Credit card number for payment + type: string + customerName: + description: Name of the customer + type: string + deliveryAddress: + description: Delivery address for the order + type: string + pizzaType: + description: Type of pizza to order + type: string + quantity: + description: Number of pizzas to order + minimum: 1 + type: integer + required: + - pizzaType + - quantity + - customerName + - deliveryAddress + - creditCardNumber + type: object + name: orderPizza diff --git a/tests/ai-workspace-cli-e2e/resources/mcp/edit/metadata.yaml b/tests/ai-workspace-cli-e2e/resources/mcp/edit/metadata.yaml new file mode 100644 index 000000000..d52b4fc19 --- /dev/null +++ b/tests/ai-workspace-cli-e2e/resources/mcp/edit/metadata.yaml @@ -0,0 +1,16 @@ +apiVersion: ai-workspace.api-platform.wso2.com/v1alpha +kind: McpMetadata +metadata: + name: mcp-proxy +spec: + displayName: mcp-proxy + version: v1.0 + # associatedGateways must live under spec for the CLI to fold it into the + # payload. These gateway handles are registered by the suite before apply. + associatedGateways: + - id: prod-eu-01 + configurations: + host: prod-eu-01.api.wso2.com + - id: prod-eu-02 + configurations: + host: prod-eu-02.api.wso2.com diff --git a/tests/ai-workspace-cli-e2e/resources/mcp/edit/runtime.yaml b/tests/ai-workspace-cli-e2e/resources/mcp/edit/runtime.yaml new file mode 100644 index 000000000..1f14a9351 --- /dev/null +++ b/tests/ai-workspace-cli-e2e/resources/mcp/edit/runtime.yaml @@ -0,0 +1,26 @@ +apiVersion: gateway.api-platform.wso2.com/v1 +kind: Mcp +metadata: + name: mcp-proxy + labels: + projectId: 019f2324-be88-7ecf-b15d-214ae1e3283d +spec: + displayName: mcp-proxy + version: v1.0 + context: /default/mcp-proxy + vhost: null + upstream: + url: https://db720294-98fd-40f4-85a1-cc6a3b65bc9a-prod.e1-us-east-azure.choreoapis.dev/godzilla/mcp-everything-server/v1.0 + auth: null + specVersion: "2025-06-18" + policies: + - executioncondition: null + name: log-message + params: + request: + headers: false + payload: false + response: + headers: false + payload: false + version: v1 \ No newline at end of file diff --git a/tests/ai-workspace-cli-e2e/steps_test.go b/tests/ai-workspace-cli-e2e/steps_test.go new file mode 100644 index 000000000..7548e4b1a --- /dev/null +++ b/tests/ai-workspace-cli-e2e/steps_test.go @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package awcli + +import ( + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/cucumber/godog" +) + +// artifactSpec describes one AI Workspace artifact kind end-to-end: how to +// scaffold it (initType), which resource files back it (resourceDir), the +// directory / resource id it maps to (dir), the CLI get/list sub-command group +// (group), and whether it is project-scoped (proxies/MCP require --project-id). +type artifactSpec struct { + initType string + dir string + resourceDir string + group string + projectScoped bool +} + +// artifacts is keyed by the friendly name used in the feature file. dir is both +// the `ap project init` display name and the resource id (metadata.name), so it +// matches the name inside the staged demo files. +var artifacts = map[string]artifactSpec{ + "llm-provider": {initType: "App-LLM-Provider", dir: "claude-provider", resourceDir: "llm-provider", group: "llm-provider", projectScoped: false}, + "llm-proxy": {initType: "LLM-Proxy", dir: "claude-proxy", resourceDir: "llm-proxy", group: "app-llm-proxy", projectScoped: true}, + "mcp-proxy": {initType: "MCP-Proxy", dir: "mcp-proxy", resourceDir: "mcp", group: "mcp-proxy", projectScoped: true}, +} + +// world holds per-scenario state: the last CLI invocation result. +type world struct { + last cliResult +} + +func initializeScenario(ctx *godog.ScenarioContext) { + w := &world{} + + ctx.Step(`^the platform-api AI Workspace backend is running$`, w.backendRunning) + ctx.Step(`^I am authenticated to the AI Workspace$`, w.authenticated) + ctx.Step(`^the "([^"]*)" project artifact is initialized$`, w.initArtifact) + ctx.Step(`^I edit the "([^"]*)" artifact$`, w.editArtifact) + ctx.Step(`^I build the "([^"]*)" artifact$`, w.buildArtifact) + ctx.Step(`^I apply the "([^"]*)" artifact$`, w.applyArtifact) + ctx.Step(`^I re-apply the "([^"]*)" artifact$`, w.applyArtifact) + ctx.Step(`^the CLI reports the "([^"]*)" artifact was created$`, w.reportedCreated) + ctx.Step(`^the CLI reports the "([^"]*)" artifact was updated$`, w.reportedUpdated) + ctx.Step(`^the "([^"]*)" artifact is retrievable from the AI Workspace$`, w.artifactRetrievable) + ctx.Step(`^the "([^"]*)" artifact is listed in the AI Workspace$`, w.artifactListed) + ctx.Step(`^the "([^"]*)" artifact is associated with gateway "([^"]*)"$`, w.artifactAssociatedWithGateway) +} + +func (w *world) backendRunning() error { + if suite.token == "" || suite.projectID == "" { + return fmt.Errorf("platform-api AI Workspace backend was not bootstrapped (token/project missing)") + } + return nil +} + +func (w *world) authenticated() error { + if suite.token == "" { + return fmt.Errorf("no AI Workspace bearer token available") + } + return nil +} + +// initArtifact scaffolds the project with `ap project init` and then replaces the +// generated files with the suite's known-good "create" demo resources. +func (w *world) initArtifact(key string) error { + spec, err := lookup(key) + if err != nil { + return err + } + + res, err := runAP(nil, "project", "init", + "--display-name", spec.dir, + "--type", spec.initType, + "--no-interactive", + ) + if err != nil { + return err + } + if res.exit != 0 { + return fmt.Errorf("ap project init (%s) failed (exit %d):\n%s", spec.initType, res.exit, res.combined()) + } + + return stage(spec, "create") +} + +// editArtifact overlays the "edit" demo variant (changed runtime + metadata that +// adds spec.associatedGateways) so the re-apply exercises the update path with +// genuinely modified content. +func (w *world) editArtifact(key string) error { + spec, err := lookup(key) + if err != nil { + return err + } + return stage(spec, "edit") +} + +// stage copies the demo resource files for the given variant ("create" or "edit") +// into the scaffolded project directory. definition.yaml is shared across +// variants and only staged on create. +func stage(spec artifactSpec, variant string) error { + projectDir := filepath.Join(suite.workspaceRoot, spec.dir) + files := []string{"metadata.yaml", "runtime.yaml"} + if variant == "create" { + files = append(files, "definition.yaml") + } + for _, name := range files { + src := filepath.Join(suite.resourcesRoot, spec.resourceDir, variant, name) + if name == "definition.yaml" { + // definition.yaml lives at the resource-dir root, shared by both variants. + src = filepath.Join(suite.resourcesRoot, spec.resourceDir, name) + } + dst := filepath.Join(projectDir, name) + if err := copyFile(src, dst); err != nil { + return fmt.Errorf("stage %s (%s) for %q: %w", name, variant, spec.dir, err) + } + } + return nil +} + +func (w *world) buildArtifact(key string) error { + spec, err := lookup(key) + if err != nil { + return err + } + res, err := runAP(nil, "ai-workspace", "build", "-f", spec.dir) + if err != nil { + return err + } + w.last = res + if res.exit != 0 { + return fmt.Errorf("ap ai-workspace build (%s) failed (exit %d):\n%s", key, res.exit, res.combined()) + } + return nil +} + +// applyArtifact runs `ap ai-workspace apply`, supplying the bearer token via the +// environment and --project-id for project-scoped kinds. It records the result so +// the create/update assertions can inspect it. +func (w *world) applyArtifact(key string) error { + spec, err := lookup(key) + if err != nil { + return err + } + args := []string{"ai-workspace", "apply", "-f", spec.dir, "--display-name", wsName, "--insecure"} + if spec.projectScoped { + args = append(args, "--project-id", suite.projectID) + } + res, err := runAP([]string{"WSO2AP_AIWORKSPACE_TOKEN=" + suite.token}, args...) + if err != nil { + return err + } + w.last = res + if res.exit != 0 { + return fmt.Errorf("ap ai-workspace apply (%s) failed (exit %d):\n%s", key, res.exit, res.combined()) + } + return nil +} + +func (w *world) reportedCreated(key string) error { + return assertContainsAll(w.last, "Status: success", "applied successfully") +} + +func (w *world) reportedUpdated(key string) error { + return assertContainsAll(w.last, "Status: success", "updated successfully") +} + +// artifactRetrievable confirms persistence by reading the artifact back through +// the CLI's own get-by-id command. +func (w *world) artifactRetrievable(key string) error { + spec, err := lookup(key) + if err != nil { + return err + } + res, err := runAP([]string{"WSO2AP_AIWORKSPACE_TOKEN=" + suite.token}, + "ai-workspace", spec.group, "get", "--id", spec.dir, "--display-name", wsName, "--insecure") + if err != nil { + return err + } + w.last = res + if res.exit != 0 { + return fmt.Errorf("ap ai-workspace %s get (%s) failed (exit %d):\n%s", spec.group, key, res.exit, res.combined()) + } + if !strings.Contains(res.combined(), spec.dir) { + return fmt.Errorf("get output for %q did not contain id %q:\n%s", key, spec.dir, res.combined()) + } + return nil +} + +// artifactListed confirms the applied artifact appears in the CLI's list command. +// Provider lists are org-scoped; proxy/MCP lists require --project-id. +func (w *world) artifactListed(key string) error { + spec, err := lookup(key) + if err != nil { + return err + } + args := []string{"ai-workspace", spec.group, "list", "--display-name", wsName, "--insecure"} + if spec.projectScoped { + args = append(args, "--project-id", suite.projectID) + } + res, err := runAP([]string{"WSO2AP_AIWORKSPACE_TOKEN=" + suite.token}, args...) + if err != nil { + return err + } + w.last = res + if res.exit != 0 { + return fmt.Errorf("ap ai-workspace %s list (%s) failed (exit %d):\n%s", spec.group, key, res.exit, res.combined()) + } + if !strings.Contains(res.combined(), spec.dir) { + return fmt.Errorf("list output for %q did not contain id %q:\n%s", key, spec.dir, res.combined()) + } + return nil +} + +// artifactAssociatedWithGateway confirms the update persisted spec.associatedGateways +// by reading the artifact back and checking the gateway handle appears in the +// server response. +func (w *world) artifactAssociatedWithGateway(key, gatewayID string) error { + spec, err := lookup(key) + if err != nil { + return err + } + res, err := runAP([]string{"WSO2AP_AIWORKSPACE_TOKEN=" + suite.token}, + "ai-workspace", spec.group, "get", "--id", spec.dir, "--display-name", wsName, "--insecure") + if err != nil { + return err + } + w.last = res + if res.exit != 0 { + return fmt.Errorf("ap ai-workspace %s get (%s) failed (exit %d):\n%s", spec.group, key, res.exit, res.combined()) + } + if !strings.Contains(res.combined(), gatewayID) { + return fmt.Errorf("get output for %q did not show associated gateway %q:\n%s", key, gatewayID, res.combined()) + } + return nil +} + +func lookup(key string) (artifactSpec, error) { + spec, ok := artifacts[key] + if !ok { + return artifactSpec{}, fmt.Errorf("unknown artifact key %q", key) + } + return spec, nil +} + +func assertContainsAll(res cliResult, substrs ...string) error { + out := res.combined() + if res.exit != 0 { + return fmt.Errorf("expected exit 0, got %d:\n%s", res.exit, out) + } + for _, s := range substrs { + if !strings.Contains(out, s) { + return fmt.Errorf("expected output to contain %q, got:\n%s", s, out) + } + } + return nil +} + +func copyFile(src, dst string) error { + data, err := os.ReadFile(src) + if err != nil { + return err + } + return os.WriteFile(dst, data, 0o644) +} diff --git a/tests/ai-workspace-cli-e2e/suite_test.go b/tests/ai-workspace-cli-e2e/suite_test.go new file mode 100644 index 000000000..0331def67 --- /dev/null +++ b/tests/ai-workspace-cli-e2e/suite_test.go @@ -0,0 +1,396 @@ +/* + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// Package awcli holds the AI Workspace CLI end-to-end suite. It boots a real +// platform-api control plane (the AI Workspace backend) over docker compose, +// then drives the real `ap` CLI binary against it: register the workspace, scaffold +// projects with `ap project init`, and publish the three AI Workspace artifact +// kinds (LLM provider, LLM proxy, MCP proxy) with `ap ai-workspace build`/`apply`, +// asserting the CLI persists each artifact and can read it back. +package awcli + +import ( + "bytes" + "context" + "crypto/tls" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/cucumber/godog" +) + +const ( + // composeProject namespaces the docker compose stack for this suite. + composeProject = "aiwscli-e2e" + // composeFile is the standalone platform-api compose in this directory. + composeFile = "docker-compose.yaml" + + // portalAuthBase is where the file-based login endpoint lives. + portalAuthBase = "/api/portal/v0.9" + // apiBase is the AI Workspace / project REST base path. + apiBase = "/api/v0.9" + + // wsName is the CLI-side name we register the platform-api under. + wsName = "it-ws" + + // pollTimeout bounds platform-api readiness polling. + pollTimeout = 180 * time.Second +) + +// platformAPI is the host-side base URL of the platform-api HTTPS listener. +var platformAPI = "https://localhost:" + envOr("PA_HOST_PORT", "9243") + +// demoGateways are the gateway handles referenced by the edit artifacts' +// spec.associatedGateways. They are registered up front so the server can +// resolve the associations (resolveAssociatedGateways rejects unknown handles). +var demoGateways = []string{"prod-eu-01", "prod-eu-02"} + +// httpClient talks to the self-signed platform-api directly (login + project +// bootstrap only — every artifact operation goes through the `ap` CLI). +var httpClient = &http.Client{ + Timeout: 30 * time.Second, + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // #nosec G402 — local self-signed cert + }, +} + +// awSuite is the shared, suite-scoped state populated in BeforeSuite and read by +// the step definitions. +type awSuite struct { + token string // admin JWT from /auth/login, reused as the AI Workspace bearer token + projectID string // server-side project handle for project-scoped kinds (proxies/MCP) + cliBin string // absolute path to the built `ap` binary + home string // isolated HOME so the CLI writes its own ~/.wso2ap/config.yaml + workspaceRoot string // scratch dir where `ap project init` scaffolds projects + resourcesRoot string // absolute path to this suite's resources/ dir +} + +var suite awSuite + +// TestFeatures is the suite entry point. Tags can be narrowed via AW_TAGS. +func TestFeatures(t *testing.T) { + status := godog.TestSuite{ + Name: "ai-workspace-cli-e2e", + TestSuiteInitializer: initializeSuite, + ScenarioInitializer: initializeScenario, + Options: &godog.Options{ + Format: "pretty", + Paths: []string{"features"}, + Tags: os.Getenv("AW_TAGS"), + Strict: true, + TestingT: t, + }, + }.Run() + if status != 0 { + t.Fatalf("godog suite failed with status %d", status) + } +} + +func initializeSuite(ctx *godog.TestSuiteContext) { + ctx.BeforeSuite(func() { + if err := bringUp(); err != nil { + tearDown() + panic(fmt.Sprintf("failed to bring up AI Workspace CLI e2e stack: %v", err)) + } + }) + ctx.AfterSuite(func() { + if os.Getenv("AW_KEEP") == "" { + tearDown() + } + }) +} + +// bringUp starts platform-api, waits for health, mints an admin token, creates a +// server-side project, verifies the `ap` binary, and registers the workspace in +// an isolated CLI config. +func bringUp() error { + if err := compose("up", "-d"); err != nil { + return fmt.Errorf("start platform-api: %w", err) + } + if err := waitHealthy(); err != nil { + return err + } + if err := login(); err != nil { + return err + } + if err := createProject(); err != nil { + return err + } + for _, gw := range demoGateways { + if err := createGateway(gw); err != nil { + return err + } + } + if err := resolveCLI(); err != nil { + return err + } + if err := setupCLIEnv(); err != nil { + return err + } + return registerWorkspace() +} + +func tearDown() { + _ = compose("down", "-v", "--remove-orphans") + if suite.home != "" { + _ = os.RemoveAll(suite.home) + } + if suite.workspaceRoot != "" { + _ = os.RemoveAll(suite.workspaceRoot) + } +} + +// compose runs `docker compose` for this suite's stack. PLATFORM_API_IMAGE and +// PA_HOST_PORT flow through from the process environment. +func compose(args ...string) error { + full := append([]string{"compose", "-p", composeProject, "-f", composeFile}, args...) + cmd := exec.Command("docker", full...) + cmd.Env = os.Environ() + if out, err := cmd.CombinedOutput(); err != nil { + return fmt.Errorf("docker compose %v: %w\n%s", args, err, out) + } + return nil +} + +func waitHealthy() error { + deadline := time.Now().Add(pollTimeout) + var lastErr error + for time.Now().Before(deadline) { + resp, err := httpClient.Get(platformAPI + "/health") + if err == nil { + body, _ := io.ReadAll(resp.Body) + resp.Body.Close() + if resp.StatusCode == http.StatusOK && bytes.Contains(body, []byte("ok")) { + return nil + } + lastErr = fmt.Errorf("status %d: %s", resp.StatusCode, body) + } else { + lastErr = err + } + time.Sleep(2 * time.Second) + } + return fmt.Errorf("platform-api did not become healthy at %s: %v", platformAPI, lastErr) +} + +// login exchanges admin/admin for a JWT via the file-based login endpoint. +func login() error { + form := url.Values{"username": {"admin"}, "password": {"admin"}} + resp, err := httpClient.PostForm(platformAPI+portalAuthBase+"/auth/login", form) + if err != nil { + return fmt.Errorf("login request: %w", err) + } + defer resp.Body.Close() + body, _ := io.ReadAll(resp.Body) + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("login failed (%d): %s", resp.StatusCode, body) + } + var out struct { + Token string `json:"token"` + } + if err := json.Unmarshal(body, &out); err != nil { + return fmt.Errorf("decode login response: %w (%s)", err, body) + } + if out.Token == "" { + return fmt.Errorf("login response had no token: %s", body) + } + suite.token = out.Token + return nil +} + +// createProject creates a server-side project and records its handle, which the +// project-scoped kinds (LLM proxy, MCP proxy) pass to `apply --project-id`. +func createProject() error { + reqBody, _ := json.Marshal(map[string]string{"displayName": "IT AI Workspace Project"}) + req, err := http.NewRequest(http.MethodPost, platformAPI+apiBase+"/projects", bytes.NewReader(reqBody)) + if err != nil { + return err + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Bearer "+suite.token) + resp, err := httpClient.Do(req) + if err != nil { + return fmt.Errorf("create project request: %w", err) + } + defer resp.Body.Close() + body, _ := io.ReadAll(resp.Body) + if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK { + return fmt.Errorf("create project failed (%d): %s", resp.StatusCode, body) + } + var out struct { + ID string `json:"id"` + } + if err := json.Unmarshal(body, &out); err != nil { + return fmt.Errorf("decode project response: %w (%s)", err, body) + } + if out.ID == "" { + return fmt.Errorf("project response had no id: %s", body) + } + suite.projectID = out.ID + return nil +} + +// createGateway registers a control-plane gateway by handle so artifacts can +// associate with it. associatedGateways references are validated server-side +// (unknown handles are rejected), so the demo handles must exist before apply. +func createGateway(handle string) error { + reqBody, _ := json.Marshal(map[string]any{ + "id": handle, + "displayName": handle, + "endpoints": []string{"http://" + handle + ".example.com"}, + "functionalityType": "regular", + }) + req, err := http.NewRequest(http.MethodPost, platformAPI+apiBase+"/gateways", bytes.NewReader(reqBody)) + if err != nil { + return err + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Bearer "+suite.token) + resp, err := httpClient.Do(req) + if err != nil { + return fmt.Errorf("create gateway %q request: %w", handle, err) + } + defer resp.Body.Close() + body, _ := io.ReadAll(resp.Body) + // Treat an already-existing gateway as success so reruns against a kept stack + // (AW_KEEP) don't fail. + if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusConflict { + return fmt.Errorf("create gateway %q failed (%d): %s", handle, resp.StatusCode, body) + } + return nil +} + +// resolveCLI locates and smoke-tests the `ap` binary (override with AP_CLI_BINARY). +func resolveCLI() error { + bin := envOr("AP_CLI_BINARY", "") + if bin == "" { + abs, err := filepath.Abs(filepath.Join("..", "..", "cli", "src", "build", "ap")) + if err != nil { + return fmt.Errorf("resolve CLI path: %w", err) + } + bin = abs + } + if _, err := os.Stat(bin); err != nil { + return fmt.Errorf("ap binary not found at %s (build it with 'make -C cli/src build-skip-tests'): %w", bin, err) + } + suite.cliBin = bin + if res, err := runAP(nil, "version"); err != nil || res.exit != 0 { + return fmt.Errorf("ap version failed (exit %d): %v\n%s%s", res.exit, err, res.stdout, res.stderr) + } + return nil +} + +// setupCLIEnv creates the isolated HOME + scratch workspace and resolves the +// resources dir. +func setupCLIEnv() error { + home, err := os.MkdirTemp("", "aiwscli-home-*") + if err != nil { + return err + } + work, err := os.MkdirTemp("", "aiwscli-work-*") + if err != nil { + return err + } + res, err := filepath.Abs("resources") + if err != nil { + return err + } + suite.home = home + suite.workspaceRoot = work + suite.resourcesRoot = res + return nil +} + +// registerWorkspace points the CLI at platform-api using oauth (bearer) auth. No +// token is stored on disk; it is supplied per-call via WSO2AP_AIWORKSPACE_TOKEN. +func registerWorkspace() error { + res, err := runAP(nil, "ai-workspace", "add", + "--display-name", wsName, + "--server", platformAPI, + "--auth", "oauth", + "--no-interactive", + ) + if err != nil { + return err + } + if res.exit != 0 { + return fmt.Errorf("ai-workspace add failed (exit %d):\n%s%s", res.exit, res.stdout, res.stderr) + } + res, err = runAP(nil, "ai-workspace", "use", "--display-name", wsName) + if err != nil { + return err + } + if res.exit != 0 { + return fmt.Errorf("ai-workspace use failed (exit %d):\n%s%s", res.exit, res.stdout, res.stderr) + } + return nil +} + +// cliResult captures a single `ap` invocation. +type cliResult struct { + stdout string + stderr string + exit int +} + +func (r cliResult) combined() string { return r.stdout + r.stderr } + +// runAP executes the `ap` binary with an isolated HOME (so its config never +// touches the developer's real ~/.wso2ap) and the given extra environment. +// A non-zero exit is returned in the result (not as a Go error); a Go error +// means the process could not run at all. +func runAP(extraEnv []string, args ...string) (cliResult, error) { + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() + + cmd := exec.CommandContext(ctx, suite.cliBin, args...) + cmd.Dir = suite.workspaceRoot + cmd.Env = append(os.Environ(), "AP_NO_COLOR=true", "HOME="+suite.home) + cmd.Env = append(cmd.Env, extraEnv...) + + var out, errb bytes.Buffer + cmd.Stdout = &out + cmd.Stderr = &errb + err := cmd.Run() + + res := cliResult{stdout: out.String(), stderr: errb.String()} + if ee, ok := err.(*exec.ExitError); ok { + res.exit = ee.ExitCode() + return res, nil + } + if err != nil { + res.exit = -1 + return res, fmt.Errorf("run ap %s: %w", strings.Join(args, " "), err) + } + return res, nil +} + +func envOr(key, def string) string { + if v := os.Getenv(key); v != "" { + return v + } + return def +}